var _____WB$wombat$assign$function_____ = function(name) {return (self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init(name)) || self[name]; }; if (!self.__WB_pmw) { self.__WB_pmw = function(obj) { this.__WB_source = obj; return this; } } { let window = _____WB$wombat$assign$function_____("window"); let self = _____WB$wombat$assign$function_____("self"); let document = _____WB$wombat$assign$function_____("document"); let location = _____WB$wombat$assign$function_____("location"); let top = _____WB$wombat$assign$function_____("top"); let parent = _____WB$wombat$assign$function_____("parent"); let frames = _____WB$wombat$assign$function_____("frames"); let opener = _____WB$wombat$assign$function_____("opener"); /* Detect-zoom * ----------- * Cross Browser Zoom and Pixel Ratio Detector * Version 1.0.4 | Apr 1 2013 * dual-licensed under the WTFPL and MIT license * Maintained by https://github/tombigel * Original developer https://github.com/yonran */ //AMD and CommonJS initialization copied from https://github.com/zohararad/audio5js (function (root, ns, factory) { "use strict"; if (typeof (module) !== 'undefined' && module.exports) { // CommonJS module.exports = factory(ns, root); } else if (typeof (define) === 'function' && define.amd) { // AMD define("factory", function () { return factory(ns, root); }); } else { root[ns] = factory(ns, root); } }(window, 'detectZoom', function () { /** * Use devicePixelRatio if supported by the browser * @return {Number} * @private */ var devicePixelRatio = function () { return window.devicePixelRatio || 1; }; /** * Fallback function to set default values * @return {Object} * @private */ var fallback = function () { return { zoom: 1, devicePxPerCssPx: 1 }; }; /** * IE 8 and 9: no trick needed! * TODO: Test on IE10 and Windows 8 RT * @return {Object} * @private **/ var ie8 = function () { var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100) / 100; return { zoom: zoom, devicePxPerCssPx: zoom * devicePixelRatio() }; }; /** * For IE10 we need to change our technique again... * thanks https://github.com/stefanvanburen * @return {Object} * @private */ var ie10 = function () { var zoom = Math.round((document.documentElement.offsetHeight / window.innerHeight) * 100) / 100; return { zoom: zoom, devicePxPerCssPx: zoom * devicePixelRatio() }; }; /** * Mobile WebKit * the trick: window.innerWIdth is in CSS pixels, while * screen.width and screen.height are in system pixels. * And there are no scrollbars to mess up the measurement. * @return {Object} * @private */ var webkitMobile = function () { var deviceWidth = (Math.abs(window.orientation) == 90) ? screen.height : screen.width; var zoom = deviceWidth / window.innerWidth; return { zoom: zoom, devicePxPerCssPx: zoom * devicePixelRatio() }; }; /** * Desktop Webkit * the trick: an element's clientHeight is in CSS pixels, while you can * set its line-height in system pixels using font-size and * -webkit-text-size-adjust:none. * device-pixel-ratio: http://www.webkit.org/blog/55/high-dpi-web-sites/ * * Previous trick (used before http://trac.webkit.org/changeset/100847): * documentElement.scrollWidth is in CSS pixels, while * document.width was in system pixels. Note that this is the * layout width of the document, which is slightly different from viewport * because document width does not include scrollbars and might be wider * due to big elements. * @return {Object} * @private */ var webkit = function () { var important = function (str) { return str.replace(/;/g, " !important;"); }; var div = document.createElement('div'); div.innerHTML = "1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9<br>0"; div.setAttribute('style', important('font: 100px/1em sans-serif; -webkit-text-size-adjust: none; text-size-adjust: none; height: auto; width: 1em; padding: 0; overflow: visible;')); // The container exists so that the div will be laid out in its own flow // while not impacting the layout, viewport size, or display of the // webpage as a whole. // Add !important and relevant CSS rule resets // so that other rules cannot affect the results. var container = document.createElement('div'); container.setAttribute('style', important('width:0; height:0; overflow:hidden; visibility:hidden; position: absolute;')); container.appendChild(div); document.body.appendChild(container); var zoom = 1000 / div.clientHeight; zoom = Math.round(zoom * 100) / 100; document.body.removeChild(container); return{ zoom: zoom, devicePxPerCssPx: zoom * devicePixelRatio() }; }; /** * no real trick; device-pixel-ratio is the ratio of device dpi / css dpi. * (Note that this is a different interpretation than Webkit's device * pixel ratio, which is the ratio device dpi / system dpi). * * Also, for Mozilla, there is no difference between the zoom factor and the device ratio. * * @return {Object} * @private */ var firefox4 = function () { var zoom = mediaQueryBinarySearch('min--moz-device-pixel-ratio', '', 0, 10, 20, 0.0001); zoom = Math.round(zoom * 100) / 100; return { zoom: zoom, devicePxPerCssPx: zoom }; }; /** * Firefox 18.x * Mozilla added support for devicePixelRatio to Firefox 18, * but it is affected by the zoom level, so, like in older * Firefox we can't tell if we are in zoom mode or in a device * with a different pixel ratio * @return {Object} * @private */ var firefox18 = function () { return { zoom: firefox4().zoom, devicePxPerCssPx: devicePixelRatio() }; }; /** * works starting Opera 11.11 * the trick: outerWidth is the viewport width including scrollbars in * system px, while innerWidth is the viewport width including scrollbars * in CSS px * @return {Object} * @private */ var opera11 = function () { var zoom = window.top.outerWidth / window.top.innerWidth; zoom = Math.round(zoom * 100) / 100; return { zoom: zoom, devicePxPerCssPx: zoom * devicePixelRatio() }; }; /** * Use a binary search through media queries to find zoom level in Firefox * @param property * @param unit * @param a * @param b * @param maxIter * @param epsilon * @return {Number} */ var mediaQueryBinarySearch = function (property, unit, a, b, maxIter, epsilon) { var matchMedia; var head, style, div; if (window.matchMedia) { matchMedia = window.matchMedia; } else { head = document.getElementsByTagName('head')[0]; style = document.createElement('style'); head.appendChild(style); div = document.createElement('div'); div.className = 'mediaQueryBinarySearch'; div.style.display = 'none'; document.body.appendChild(div); matchMedia = function (query) { style.sheet.insertRule('@media ' + query + '{.mediaQueryBinarySearch ' + '{text-decoration: underline} }', 0); var matched = getComputedStyle(div, null).textDecoration == 'underline'; style.sheet.deleteRule(0); return {matches: matched}; }; } var ratio = binarySearch(a, b, maxIter); if (div) { head.removeChild(style); document.body.removeChild(div); } return ratio; function binarySearch(a, b, maxIter) { var mid = (a + b) / 2; if (maxIter <= 0 || b - a < epsilon) { return mid; } var query = "(" + property + ":" + mid + unit + ")"; if (matchMedia(query).matches) { return binarySearch(mid, b, maxIter - 1); } else { return binarySearch(a, mid, maxIter - 1); } } }; /** * Generate detection function * @private */ var detectFunction = (function () { var func = fallback; //IE8+ if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { func = ie8; } // IE10+ / Touch else if (window.navigator.msMaxTouchPoints) { func = ie10; } //Mobile Webkit else if ('orientation' in window && typeof document.body.style.webkitMarquee === 'string') { func = webkitMobile; } //WebKit else if (typeof document.body.style.webkitMarquee === 'string') { func = webkit; } //Opera else if (navigator.userAgent.indexOf('Opera') >= 0) { func = opera11; } //Last one is Firefox //FF 18.x else if (window.devicePixelRatio) { func = firefox18; } //FF 4.0 - 17.x else if (firefox4().zoom > 0.001) { func = firefox4; } return func; }()); return ({ /** * Ratios.zoom shorthand * @return {Number} Zoom level */ zoom: function () { return detectFunction().zoom; }, /** * Ratios.devicePxPerCssPx shorthand * @return {Number} devicePxPerCssPx level */ device: function () { return detectFunction().devicePxPerCssPx; } }); })); var wpcom_img_zoomer = { clientHintSupport: { gravatar: false, files: false, photon: false, mshots: false, staticAssets: false, latex: false, imgpress: false, }, useHints: false, zoomed: false, timer: null, interval: 1000, // zoom polling interval in millisecond // Should we apply width/height attributes to control the image size? imgNeedsSizeAtts: function( img ) { // Do not overwrite existing width/height attributes. if ( img.getAttribute('width') !== null || img.getAttribute('height') !== null ) return false; // Do not apply the attributes if the image is already constrained by a parent element. if ( img.width < img.naturalWidth || img.height < img.naturalHeight ) return false; return true; }, hintsFor: function( service ) { if ( this.useHints === false ) { return false; } if ( this.hints() === false ) { return false; } if ( typeof this.clientHintSupport[service] === "undefined" ) { return false; } if ( this.clientHintSupport[service] === true ) { return true; } return false; }, hints: function() { try { var chrome = window.navigator.userAgent.match(/\sChrome\/([0-9]+)\.[.0-9]+\s/) if (chrome !== null) { var version = parseInt(chrome[1], 10) if (isNaN(version) === false && version >= 46) { return true } } } catch (e) { return false } return false }, init: function() { var t = this; try{ t.zoomImages(); t.timer = setInterval( function() { t.zoomImages(); }, t.interval ); } catch(e){ } }, stop: function() { if ( this.timer ) clearInterval( this.timer ); }, getScale: function() { var scale = detectZoom.device(); // Round up to 1.5 or the next integer below the cap. if ( scale <= 1.0 ) scale = 1.0; else if ( scale <= 1.5 ) scale = 1.5; else if ( scale <= 2.0 ) scale = 2.0; else if ( scale <= 3.0 ) scale = 3.0; else if ( scale <= 4.0 ) scale = 4.0; else scale = 5.0; return scale; }, shouldZoom: function( scale ) { var t = this; // Do not operate on hidden frames. if ( "innerWidth" in window && !window.innerWidth ) return false; // Don't do anything until scale > 1 if ( scale == 1.0 && t.zoomed == false ) return false; return true; }, zoomImages: function() { var t = this; var scale = t.getScale(); if ( ! t.shouldZoom( scale ) ){ return; } t.zoomed = true; // Loop through all the <img> elements on the page. var imgs = document.getElementsByTagName("img"); for ( var i = 0; i < imgs.length; i++ ) { // Wait for original images to load if ( "complete" in imgs[i] && ! imgs[i].complete ) continue; // Skip images that have srcset attributes. if ( imgs[i].hasAttribute('srcset') ) { continue; } // Skip images that don't need processing. var imgScale = imgs[i].getAttribute("scale"); if ( imgScale == scale || imgScale == "0" ) continue; // Skip images that have already failed at this scale var scaleFail = imgs[i].getAttribute("scale-fail"); if ( scaleFail && scaleFail <= scale ) continue; // Skip images that have no dimensions yet. if ( ! ( imgs[i].width && imgs[i].height ) ) continue; // Skip images from Lazy Load plugins if ( ! imgScale && imgs[i].getAttribute("data-lazy-src") && (imgs[i].getAttribute("data-lazy-src") !== imgs[i].getAttribute("src"))) continue; if ( t.scaleImage( imgs[i], scale ) ) { // Mark the img as having been processed at this scale. imgs[i].setAttribute("scale", scale); } else { // Set the flag to skip this image. imgs[i].setAttribute("scale", "0"); } } }, scaleImage: function( img, scale ) { var t = this; var newSrc = img.src; var isFiles = false; var isLatex = false; var isPhoton = false; // Skip slideshow images if ( img.parentNode.className.match(/slideshow-slide/) ) return false; // Skip CoBlocks Lightbox images if ( img.parentNode.className.match(/coblocks-lightbox__image/) ) return false; // Scale gravatars that have ?s= or ?size= if ( img.src.match( /^https?:\/\/([^\/]*\.)?gravatar\.com\/.+[?&](s|size)=/ ) ) { if ( this.hintsFor( "gravatar" ) === true ) { return false; } newSrc = img.src.replace( /([?&](s|size)=)(\d+)/, function( $0, $1, $2, $3 ) { // Stash the original size var originalAtt = "originals", originalSize = img.getAttribute(originalAtt); if ( originalSize === null ) { originalSize = $3; img.setAttribute(originalAtt, originalSize); if ( t.imgNeedsSizeAtts( img ) ) { // Fix width and height attributes to rendered dimensions. img.width = img.width; img.height = img.height; } } // Get the width/height of the image in CSS pixels var size = img.clientWidth; // Convert CSS pixels to device pixels var targetSize = Math.ceil(img.clientWidth * scale); // Don't go smaller than the original size targetSize = Math.max( targetSize, originalSize ); // Don't go larger than the service supports targetSize = Math.min( targetSize, 512 ); return $1 + targetSize; }); } // Scale mshots that have width else if ( img.src.match(/^https?:\/\/([^\/]+\.)*(wordpress|wp)\.com\/mshots\/.+[?&]w=\d+/) ) { if ( this.hintsFor( "mshots" ) === true ) { return false; } newSrc = img.src.replace( /([?&]w=)(\d+)/, function($0, $1, $2) { // Stash the original size var originalAtt = 'originalw', originalSize = img.getAttribute(originalAtt); if ( originalSize === null ) { originalSize = $2; img.setAttribute(originalAtt, originalSize); if ( t.imgNeedsSizeAtts( img ) ) { // Fix width and height attributes to rendered dimensions. img.width = img.width; img.height = img.height; } } // Get the width of the image in CSS pixels var size = img.clientWidth; // Convert CSS pixels to device pixels var targetSize = Math.ceil(size * scale); // Don't go smaller than the original size targetSize = Math.max( targetSize, originalSize ); // Don't go bigger unless the current one is actually lacking if ( scale > img.getAttribute("scale") && targetSize <= img.naturalWidth ) targetSize = $2; if ( $2 != targetSize ) return $1 + targetSize; return $0; }); // Update height attribute to match width newSrc = newSrc.replace( /([?&]h=)(\d+)/, function($0, $1, $2) { if ( newSrc == img.src ) { return $0; } // Stash the original size var originalAtt = 'originalh', originalSize = img.getAttribute(originalAtt); if ( originalSize === null ) { originalSize = $2; img.setAttribute(originalAtt, originalSize); } // Get the height of the image in CSS pixels var size = img.clientHeight; // Convert CSS pixels to device pixels var targetSize = Math.ceil(size * scale); // Don't go smaller than the original size targetSize = Math.max( targetSize, originalSize ); // Don't go bigger unless the current one is actually lacking if ( scale > img.getAttribute("scale") && targetSize <= img.naturalHeight ) targetSize = $2; if ( $2 != targetSize ) return $1 + targetSize; return $0; }); } // Scale simple imgpress queries (s0.wp.com) that only specify w/h/fit else if ( img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/imgpress\?(.+)/) ) { if ( this.hintsFor( "imgpress" ) === true ) { return false; } var imgpressSafeFunctions = ["zoom", "url", "h", "w", "fit", "filter", "brightness", "contrast", "colorize", "smooth", "unsharpmask"]; // Search the query string for unsupported functions. var qs = RegExp.$3.split('&'); for ( var q in qs ) { q = qs[q].split('=')[0]; if ( imgpressSafeFunctions.indexOf(q) == -1 ) { return false; } } // Fix width and height attributes to rendered dimensions. img.width = img.width; img.height = img.height; // Compute new src if ( scale == 1 ) newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?'); else newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?zoom=' + scale + '&'); } // Scale files.wordpress.com, LaTeX, or Photon images (i#.wp.com) else if ( ( isFiles = img.src.match(/^https?:\/\/([^\/]+)\.files\.wordpress\.com\/.+[?&][wh]=/) ) || ( isLatex = img.src.match(/^https?:\/\/([^\/.]+\.)*(wp|wordpress)\.com\/latex\.php\?(latex|zoom)=(.+)/) ) || ( isPhoton = img.src.match(/^https?:\/\/i[\d]{1}\.wp\.com\/(.+)/) ) ) { if ( false !== isFiles && this.hintsFor( "files" ) === true ) { return false } if ( false !== isLatex && this.hintsFor( "latex" ) === true ) { return false } if ( false !== isPhoton && this.hintsFor( "photon" ) === true ) { return false } // Fix width and height attributes to rendered dimensions. img.width = img.width; img.height = img.height; // Compute new src if ( scale == 1 ) { newSrc = img.src.replace(/\?(zoom=[^&]+&)?/, '?'); } else { newSrc = img.src; var url_var = newSrc.match( /([?&]w=)(\d+)/ ); if ( url_var !== null && url_var[2] ) { newSrc = newSrc.replace( url_var[0], url_var[1] + img.width ); } url_var = newSrc.match( /([?&]h=)(\d+)/ ); if ( url_var !== null && url_var[2] ) { newSrc = newSrc.replace( url_var[0], url_var[1] + img.height ); } var zoom_arg = '&zoom=2'; if ( !newSrc.match( /\?/ ) ) { zoom_arg = '?zoom=2'; } img.setAttribute( 'srcset', newSrc + zoom_arg + ' ' + scale + 'x' ); } } // Scale static assets that have a name matching *-1x.png or *@1x.png else if ( img.src.match(/^https?:\/\/[^\/]+\/.*[-@]([12])x\.(gif|jpeg|jpg|png)(\?|$)/) ) { if ( this.hintsFor( "staticAssets" ) === true ) { return false; } // Fix width and height attributes to rendered dimensions. img.width = img.width; img.height = img.height; var currentSize = RegExp.$1, newSize = currentSize; if ( scale <= 1 ) newSize = 1; else newSize = 2; if ( currentSize != newSize ) newSrc = img.src.replace(/([-@])[12]x\.(gif|jpeg|jpg|png)(\?|$)/, '$1'+newSize+'x.$2$3'); } else { return false; } // Don't set img.src unless it has changed. This avoids unnecessary reloads. if ( newSrc != img.src ) { // Store the original img.src var prevSrc, origSrc = img.getAttribute("src-orig"); if ( !origSrc ) { origSrc = img.src; img.setAttribute("src-orig", origSrc); } // In case of error, revert img.src prevSrc = img.src; img.onerror = function(){ img.src = prevSrc; if ( img.getAttribute("scale-fail") < scale ) img.setAttribute("scale-fail", scale); img.onerror = null; }; // Finally load the new image img.src = newSrc; } return true; } }; wpcom_img_zoomer.init(); ; /** * Comment Likes - JavaScript * * This handles liking and unliking comments, as well as viewing who has * liked a particular comment. * * @dependency Swipe (dynamically loaded when needed) * * @package Comment_Likes * @subpackage JavaScript */ (function () { function init() { let extWin; let extWinCheck; let commentLikeEvent; // Only run once. if (window.comment_likes_loaded) { return; } window.comment_likes_loaded = true; // Client-side cache of who liked a particular comment to avoid // having to hit the server multiple times for the same data. const commentLikeCache = {}; let swipeLibPromise; // Load the Swipe library, if it's not already loaded. function swipeLibLoader() { if (!swipeLibPromise) { swipeLibPromise = new Promise((resolve, reject) => { if (window.Swipe) { resolve(window.Swipe); } else { const swipeScript = document.createElement('script'); swipeScript.src = comment_like_text.swipeUrl; swipeScript.async = true; document.body.appendChild(swipeScript); swipeScript.addEventListener('load', () => resolve(window.Swipe)); swipeScript.addEventListener('error', error => reject(error)); } }); } return swipeLibPromise; } /** * Parse the comment ID from a comment like link. */ function getCommentId(link) { const commentId = link && link.getAttribute('href') && link.getAttribute('href').split('like_comment='); return commentId[1].split('&_wpnonce=')[0]; } /** * Handle an ajax action on the comment like link. */ function handleLinkAction(link, action, commentId, callback) { const nonce = link && link.getAttribute('href') && link.getAttribute('href').split('_wpnonce=')[1]; fetch('/wp-admin/admin-ajax.php', { method: 'POST', body: new URLSearchParams({ action: action, _wpnonce: nonce, like_comment: commentId, blog_id: Number(link.dataset.blog), }), headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'X-Requested-With': 'XMLHttpRequest', Accept: 'application/json', 'cache-control': 'no-cache', pragma: 'no-cache', }, }) .then(response => response.json()) .then(callback); } function startPolling() { // Append cookie polling login iframe to this window to wait for user to finish logging in (or cancel) const loginIframe = document.createElement('iframe'); loginIframe.id = 'wp-login-polling-iframe'; loginIframe.src = 'https://web.archive.org/web/20211231085626/https://wordpress.com/public.api/connect/?iframe=true'; document.body.appendChild(loginIframe); loginIframe.style.display = 'none'; } function stopPolling() { const iframe = document.querySelector('#wp-login-polling-iframe'); if (iframe) { iframe.remove(); } } function hide(el) { if (el && el.style) { el.style.display = 'none'; } } function show(el) { if (el && el.style) { el.style.removeProperty('display'); } } // Overlay used for displaying comment like info. class Overlay { constructor() { // Overlay element. this.el = document.createElement('div'); this.el.classList.add('comment-likes-overlay'); document.body.appendChild(this.el); hide(this.el); this.el.addEventListener('mouseenter', () => { // Don't hide the overlay if the user is mousing over it. overlay.cancelHide(); }); this.el.addEventListener('mouseleave', () => overlay.requestHide()); // Inner contents of overlay. this.innerEl = null; // Instance of the Swipe library. this.swipe = null; // Timeout used for hiding the overlay. this.hideTimeout = null; } // Initialise the overlay for use, removing any old content. clear() { // Unload any previous instance of Swipe (to avoid leaking a global // event handler). This is done before clearing the contents of the // overlay because Swipe expects the slides to still be present. if (this.swipe) { this.swipe.kill(); this.swipe = null; } this.el.innerHTML = ''; this.innerEl = document.createElement('div'); this.innerEl.classList.add('inner'); this.el.appendChild(this.innerEl); } /** * Construct a list (<ul>) of user (gravatar, name) details. * * @param data liker data returned from the server * @param klass CSS class to apply to the <ul> element * @param start index of user to start at * @param length number of users to include in the list * * @return A container element with the list */ getUserBits(data, klass, start, length) { start = start || 0; let last = start + (length || data.length); last = last > data.length ? data.length : last; const container = document.createElement('div'); container.classList.add('liker-list'); let html = `<ul class="${klass || ''}">`; for (let i = start; i < last; ++i) { const user = data[i]; html += ` <li> <a rel="nofollow" title="${user.display_name_esc}" href="${user.profile_url_esc}"> <img src="${user.avatar_url_esc}" alt="${user.display_name_esc}" /> <span class="user-name">${user.display_name_esc}</span> </a> </li> `; } html += '</ul>'; container.innerHTML = html; return container; } /** * Render the display of who has liked this comment. The type of * display depends on how many people have liked the comment. * If more than 10 people have liked the comment, this function * renders navigation controls and sets up the Swipe library for * changing between pages. * * @param link the element over which the user is hovering * @param data the results retrieved from the server */ showLikes(link, data) { this.clear(); link.dataset.likeCount = data.length; if (data.length === 0) { // No likers after all. hide(this.el); return; } this.innerEl.style.padding = '12px'; if (data.length < 6) { // Only one column needed. this.innerEl.style.maxWidth = '200px'; this.innerEl.innerHTML = ''; this.innerEl.appendChild(this.getUserBits(data, 'single')); this.setPosition(link); } else if (data.length < 11) { // Two columns, but only one page. this.innerEl.innerHTML = ''; this.innerEl.appendChild(this.getUserBits(data, 'double')); this.setPosition(link); } else { // Multiple pages. this.renderLikesWithPagination(data, link); } } /** * Render multiple pages of likes with pagination controls. * This function is intended to be called by `showLikes` above. * * @param data the results retrieved from the server */ renderLikesWithPagination(data, link) { swipeLibLoader().then(() => { const page_count = Math.ceil(data.length / 10); // Swipe requires two nested containers. const swipe = document.createElement('div'); swipe.classList.add('swipe'); this.innerEl.appendChild(swipe); const wrap = document.createElement('div'); wrap.classList.add('swipe-wrap'); swipe.appendChild(wrap); for (let i = 0; i < page_count; ++i) { wrap.appendChild(this.getUserBits(data, 'double', i * 10, 10)); } /** * Navigation controls. * This is based on the Newdash controls found in * reader/recommendations-templates.php */ const nav = document.createElement('nav'); nav.classList.add('slider-nav'); let navContents = ` <a href="#" class="prev"> <span class="noticon noticon-previous" title="Previous" alt="<"></span> </a> <span class="position"> `; for (let i = 0; i < page_count; ++i) { navContents += `<em data-page="${i}" class="${i === 0 ? 'on' : ''}">•</em>`; } navContents += ` </span> <a href="#" class="next"> <span class="noticon noticon-next" title="Next" alt=">"></span> </a> `; this.innerEl.appendChild(nav); nav.innerHTML = navContents; /** Set up Swipe. **/ // Swipe cannot be set up successfully unless its container // is visible, so we show it now. show(this.el); this.setPosition(link); this.swipe = new Swipe(swipe, { callback: function (pos) { // Update the pagination indicators. // // If there are exactly two pages, Swipe has a weird // special case where it duplicates both pages and // can return index 2 and 3 even though those aren't // real pages (see swipe.js, line 47). To deal with // this, we use the expression `pos % page_count`. pos = pos % page_count; nav.querySelectorAll('em').forEach(em => { const page = Number(em.dataset.page); em.setAttribute('class', pos === page ? 'on' : ''); }); }, }); nav.querySelectorAll('em').forEach(em => { em.addEventListener('click', e => { // Go to the page corresponding to the indicator clicked. this.swipe.slide(Number(em.dataset.page)); e.preventDefault(); }); }); // Previous and next buttons. nav.querySelector('.prev').addEventListener('click', e => { this.swipe.prev(); e.preventDefault(); }); nav.querySelector('.next').addEventListener('click', e => { this.swipe.next(); e.preventDefault(); }); }); } /** * Open the overlay and show a loading message. */ showLoadingMessage(link) { this.clear(); this.innerEl.textContent = comment_like_text.loading; this.setPosition(link); } /** * Position the overlay near the current comment. * * @param link element near which to position the overlay */ setPosition(link) { // Prepare a down arrow icon for the bottom of the overlay. const icon = document.createElement('span'); this.el.appendChild(icon); icon.classList.add('icon', 'noticon', 'noticon-downarrow'); icon.style.textShadow = '0px 1px 1px rgb(223, 223, 223)'; const rect = link.getBoundingClientRect(); const win = document.defaultView; const offset = { top: rect.top + win.scrollY, left: rect.left + win.scrollX, }; // Take measurements with the element fully visible. show(this.el); let left = offset.left - (this.el.offsetWidth - link.offsetWidth) / 2; left = left < 5 ? 5 : left; let top = offset.top - this.el.offsetHeight + 5; hide(this.el); const adminBar = document.querySelector('#wpadminbar'); // Check if the overlay would appear off the screen. if (top < win.scrollY + ((adminBar && adminBar.offsetHeight) || 0)) { // We'll display the overlay beneath the link instead. top = offset.top + link.offsetHeight; // Instead of using the down arrow icon, use an up arrow. icon.remove(); this.el.prepend(icon); icon.classList.remove('noticon-downarrow'); icon.classList.add('noticon-uparrow'); icon.style.textShadow = '0px -1px 1px rgb(223, 223, 223)'; icon.style.verticalAlign = 'bottom'; } this.el.style.left = `${left}px`; this.el.style.top = `${top}px`; show(this.el); // The height of the arrow icon differs slightly between browsers, // so we compute the margin here to make sure it isn't disjointed // from the overlay. icon.style.marginTop = `${icon.scrollHeight - 26}px`; icon.style.marginBottom = `${20 - icon.scrollHeight}px`; // Position the arrow to be horizontally centred on the link. icon.style.paddingLeft = `${ offset.left - left + (link.offsetWidth - icon.scrollWidth) / 2 }px`; } /** * Return whether the overlay is visible. */ isVisible() { return this.el.style.getPropertyValue('display') !== 'none'; } /** * Request that the overlay be hidden after a short delay. */ requestHide() { if (this.hideTimeout !== null) { return; } this.hideTimeout = setTimeout(() => { hide(this.el); this.clear(); }, 300); } /** * Cancel a request to hide the overlay. */ cancelHide() { if (this.hideTimeout !== null) { clearTimeout(this.hideTimeout); this.hideTimeout = null; } } } // Overlay used for displaying comment like info. const overlay = new Overlay(); // The most recent comment for which the user has requested to see // who liked it. var relevantComment; // Precache after this timeout. var precacheTimeout = null; /** * Fetch the like data for a particular comment. */ function fetchLikeData(link, commentId) { commentLikeCache[commentId] = null; const container = link && link.parentElement && link.parentElement.parentElement; const star = container.querySelector('a.comment-like-link'); star && handleLinkAction(star, 'view_comment_likes', commentId, data => { // Populate the cache. commentLikeCache[commentId] = data; // Only show the overlay if the user is interested. if (overlay.isVisible() && relevantComment === commentId) { overlay.showLikes(link, data); } }); } function readCookie(c) { const nameEQ = c + '='; const cookieStrings = document.cookie.split(';'); for (let i = 0; i < cookieStrings.length; i++) { let cookieString = cookieStrings[i]; while (cookieString.charAt(0) === ' ') { cookieString = cookieString.substring(1, cookieString.length); } if (cookieString.indexOf(nameEQ) === 0) { const chunk = cookieString.substring(nameEQ.length, cookieString.length); const pairs = chunk.split('&'); const cookieData = {}; for (let num = pairs.length - 1; num >= 0; num--) { const pair = pairs[num].split('='); cookieData[pair[0]] = decodeURIComponent(pair[1]); } return cookieData; } } return null; } function getServiceData() { const data = readCookie('wpc_wpc'); if (data === null || typeof data.access_token === 'undefined' || !data.access_token) { return false; } return data; } function readMessage(msg) { const event = msg.data; if (typeof event.event === 'undefined') { return; } if (event.event === 'login' && event.success) { extWinCheck = setInterval(function () { if (!extWin || extWin.closed) { clearInterval(extWinCheck); if (getServiceData()) { // Load page in an iframe to get the current comment nonce const nonceIframe = document.createElement('iframe'); nonceIframe.id = 'wp-login-comment-nonce-iframe'; nonceIframe.style.display = 'none'; nonceIframe.src = commentLikeEvent + ''; document.body.appendChild(nonceIframe); const commentLikeId = (commentLikeEvent + '') .split('like_comment=')[1] .split('&_wpnonce=')[0]; let c; // Set a 5 second timeout to redirect to the comment page without doing the Like as a fallback const commentLikeTimeout = setTimeout(() => { window.location = commentLikeEvent; }, 5000); // Check for a new nonced redirect and use that if available before timing out const commentLikeCheck = setInterval(() => { const iframe = document.querySelector('#wp-login-comment-nonce-iframe'); if (iframe) { c = iframe.querySelector(`#comment-like-${commentLikeId} .comment-like-link`); } if (c && typeof c.href !== 'undefined') { clearTimeout(commentLikeTimeout); clearInterval(commentLikeCheck); window.location = c.href; } }, 100); } } }, 100); if (extWin) { if (!extWin.closed) { extWin.close(); } extWin = false; } stopPolling(); } } if (typeof window.postMessage !== 'undefined') { window.addEventListener('message', e => { let message = e && e.data; if (typeof message === 'string') { try { message = JSON.parse(message); } catch (err) { return; } } const type = message && message.type; if (type === 'loginMessage') { readMessage(message); } }); } document.body.addEventListener('click', e => { let target = e.target; // Don't do anything when clicking on the "X people" link. if (target.matches('p.comment-likes a.view-likers')) { e.preventDefault(); return; } // Retrieve the surrounding paragraph to the star, if it hasn't been liked. const notLikedPar = target.closest('p.comment-not-liked'); // Return if not clicking on star or surrounding paragraph. if (!target.matches('a.comment-like-link') && !notLikedPar) { return; } // When a comment hasn't been liked, make the text clickable, too. if (notLikedPar) { target = notLikedPar.querySelector('a.comment-like-link'); if (!target) { return; } } if (target.classList.contains('needs-login')) { e.preventDefault(); commentLikeEvent = target; if (extWin) { if (!extWin.closed) { extWin.close(); } extWin = false; } stopPolling(); const url = 'https://web.archive.org/web/20211231085626/https://wordpress.com/public.api/connect/?action=request&service=wordpress'; extWin = window.open( url, 'likeconn', 'status=0,toolbar=0,location=1,menubar=0,directories=0,resizable=1,scrollbars=1,height=560,width=500' ); startPolling(); return false; } // Record that the user likes or does not like this comment. const commentId = getCommentId(target); target.classList.add('loading'); let commentEl = document.querySelector(`p#comment-like-${commentId}`); // Determine whether to like or unlike based on whether the comment is // currently liked. const action = commentEl && commentEl.dataset.liked === 'comment-liked' ? 'unlike_comment' : 'like_comment'; handleLinkAction(target, action, commentId, data => { // Invalidate the like cache for this comment. delete commentLikeCache[commentId]; const countEl = document.querySelector(`#comment-like-count-${data.context}`); if (countEl) { countEl.innerHTML = data.display; } commentEl = document.querySelector(`p#comment-like-${data.context}`); if (action === 'like_comment') { commentEl.classList.remove('comment-not-liked'); commentEl.classList.add('comment-liked'); commentEl.dataset.liked = 'comment-liked'; } else { commentEl.classList.remove('comment-liked'); commentEl.classList.add('comment-not-liked'); commentEl.dataset.liked = 'comment-not-liked'; } // Prefetch new data for this comment (if there are likers left). const parent = target.closest('.comment-likes'); const link = parent && parent.querySelector('a.view-likers'); if (link) { fetchLikeData(link, commentId); } target.classList.remove('loading'); }); e.preventDefault(); e.stopPropagation(); }); document.body.addEventListener( 'mouseenter', function (e) { if (!e.target.matches('p.comment-likes a.view-likers')) { return; } // Show the user a list of who has liked this comment. const link = e.target; if (Number(link.dataset.likeCount || 0) === 0) { // No one has liked this comment. return; } // Don't hide the overlay. overlay.cancelHide(); // Get the comment ID. const container = link.parentElement && link.parentElement.parentElement; const star = container && container.querySelector('a.comment-like-link'); const commentId = star && getCommentId(star); relevantComment = commentId; // Check if the list of likes for this comment is already in // the cache. if (commentId in commentLikeCache) { const entry = commentLikeCache[commentId]; // Only display the likes if the ajax request is // actually done. if (entry !== null) { overlay.showLikes(link, entry); } else { // Make sure the overlay is visible (in case // the user moved the mouse away while loading // but then came back before it finished // loading). overlay.showLoadingMessage(link); } return; } // Position the "Loading..." overlay. overlay.showLoadingMessage(link); // Fetch the data. fetchLikeData(link, commentId); }, true ); document.body.addEventListener( 'mouseleave', e => { if (!e.target.matches('p.comment-likes a.view-likers')) { return; } // User has moved cursor away - hide the overlay. overlay.requestHide(); }, true ); document.body.addEventListener( 'mouseenter', e => { if (!e.target.matches('.comment') || !e.target.querySelector('a.comment-like-link')) { return; } // User is moving over a comment - precache the comment like data. if (precacheTimeout !== null) { clearTimeout(precacheTimeout); precacheTimeout = null; } const star = e.target.querySelector('a.comment-like-link'); const parent = star.closest('.comment-likes'); const link = parent && parent.querySelector('a.view-likers'); if (!link || Number(link.dataset.likeCount || 0) === 0) { // No likes. return; } const commentId = getCommentId(star); if (commentId in commentLikeCache) { // Already in cache. return; } precacheTimeout = setTimeout(() => { precacheTimeout = null; if (commentId in commentLikeCache) { // Was cached in the interim. return; } fetchLikeData(link, commentId); }, 1000); }, true ); } if (document.readyState !== 'loading') { init(); } else { document.addEventListener('DOMContentLoaded', init); } })(); ; !function(e){var t={};function n(o){if(t[o])return t[o].exports;var c=t[o]={i:o,l:!1,exports:{}};return e[o].call(c.exports,c,c.exports,n),c.l=!0,c.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var c in e)n.d(o,c,function(t){return e[t]}.bind(null,c));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=269)}({269:function(e,t){!function(){"use strict";const{closeLabel:e,leftLabel:t,rightLabel:n}=coblocksLigthboxData,o=document.getElementsByClassName("has-lightbox");Array.from(o).forEach((function(o,c){o.className+=" lightbox-"+c+" ",function(o){const c=document.createElement("div");c.setAttribute("class","coblocks-lightbox");const r=document.createElement("div");r.setAttribute("class","coblocks-lightbox__background");const i=document.createElement("div");i.setAttribute("class","coblocks-lightbox__heading");const l=document.createElement("button");l.setAttribute("class","coblocks-lightbox__close"),l.setAttribute("aria-label",e);const a=document.createElement("span");a.setAttribute("class","coblocks-lightbox__count");const s=document.createElement("div");s.setAttribute("class","coblocks-lightbox__image");const u=document.createElement("img"),b=document.createElement("figcaption");b.setAttribute("class","coblocks-lightbox__caption");const d=document.createElement("button");d.setAttribute("class","coblocks-lightbox__arrow coblocks-lightbox__arrow--left");const g=document.createElement("button");g.setAttribute("class","coblocks-lightbox__arrow coblocks-lightbox__arrow--right");const m=document.createElement("div");m.setAttribute("class","arrow-right"),m.setAttribute("aria-label",n);const f=document.createElement("div");f.setAttribute("class","arrow-left"),f.setAttribute("aria-label",t);const h=[`.has-lightbox.lightbox-${o} > :not(.carousel-nav) figure img`,`figure.has-lightbox.lightbox-${o} > img`,`.has-lightbox.lightbox-${o} > figure[class^="align"] img`].join(", "),p=[`.has-lightbox.lightbox-${o} > :not(.carousel-nav) figure figcaption`].join(", "),x=document.querySelectorAll(h),y=document.querySelectorAll(p);let k;i.append(a,l),s.append(u,b),d.append(f),g.append(m),c.append(r,i,s,d,g),x.length>0&&(document.getElementsByTagName("BODY")[0].append(c),1===x.length&&(g.remove(),d.remove()));y.length>0&&Array.from(y).forEach((function(e,t){e.addEventListener("click",(function(){E(t)}))}));Array.from(x).forEach((function(e,t){e.closest("figure").addEventListener("click",(function(){E(t)}))})),d.addEventListener("click",(function(){k=0===k?x.length-1:k-1,E(k)})),g.addEventListener("click",(function(){k=k===x.length-1?0:k+1,E(k)})),r.addEventListener("click",(function(){c.style.display="none"})),l.addEventListener("click",(function(){c.style.display="none"}));const v={preloaded:!1,setPreloadImages:()=>{v.preloaded||(v.preloaded=!0,Array.from(x).forEach((function(e,t){v["img-"+t]=new window.Image,v["img-"+t].src=e.attributes.src.value,v["img-"+t]["data-caption"]=x[t]&&x[t].nextElementSibling?function(e){let t=e.nextElementSibling;for(;t;){if(t.matches("figcaption"))return t.innerHTML;t=t.nextElementSibling}return""}(x[t]):""})),document.onkeydown=function(e){if(void 0!==c&&"none"!==c)switch((e=e||window.event).keyCode){case 27:l.click();break;case 37:case 65:d.click();break;case 39:case 68:g.click()}})}};function E(e){v.setPreloadImages(),k=e,c.style.display="flex",r.style.backgroundImage=`url(${v["img-"+k].src})`,u.src=v["img-"+k].src,b.innerHTML=v["img-"+k]["data-caption"],a.textContent=`${k+1} / ${x.length}`}}(c)}))}()}});; /* globals JSON */ ( function () { var eventName = 'wpcom_masterbar_click'; var linksTracksEvents = { // top level items 'wp-admin-bar-blog' : 'my_sites', 'wp-admin-bar-newdash' : 'reader', 'wp-admin-bar-ab-new-post' : 'write_button', 'wp-admin-bar-my-account' : 'my_account', 'wp-admin-bar-notes' : 'notifications', // my sites - top items 'wp-admin-bar-switch-site' : 'my_sites_switch_site', 'wp-admin-bar-blog-info' : 'my_sites_site_info', 'wp-admin-bar-site-view' : 'my_sites_view_site', 'wp-admin-bar-blog-stats' : 'my_sites_site_stats', 'wp-admin-bar-plan' : 'my_sites_plan', 'wp-admin-bar-plan-badge' : 'my_sites_plan_badge', // my sites - manage 'wp-admin-bar-edit-page' : 'my_sites_manage_site_pages', 'wp-admin-bar-new-page-badge' : 'my_sites_manage_add_page', 'wp-admin-bar-edit-post' : 'my_sites_manage_blog_posts', 'wp-admin-bar-new-post-badge' : 'my_sites_manage_add_post', 'wp-admin-bar-edit-attachment' : 'my_sites_manage_media', 'wp-admin-bar-new-attachment-badge' : 'my_sites_manage_add_media', 'wp-admin-bar-comments' : 'my_sites_manage_comments', 'wp-admin-bar-edit-jetpack-testimonial' : 'my_sites_manage_testimonials', 'wp-admin-bar-new-jetpack-testimonial' : 'my_sites_manage_add_testimonial', 'wp-admin-bar-edit-jetpack-portfolio' : 'my_sites_manage_portfolio', 'wp-admin-bar-new-jetpack-portfolio' : 'my_sites_manage_add_portfolio', // my sites - personalize 'wp-admin-bar-themes' : 'my_sites_personalize_themes', 'wp-admin-bar-cmz' : 'my_sites_personalize_themes_customize', // my sites - configure 'wp-admin-bar-sharing' : 'my_sites_configure_sharing', 'wp-admin-bar-people' : 'my_sites_configure_people', 'wp-admin-bar-people-add' : 'my_sites_configure_people_add_button', 'wp-admin-bar-plugins' : 'my_sites_configure_plugins', 'wp-admin-bar-domains' : 'my_sites_configure_domains', 'wp-admin-bar-domains-add' : 'my_sites_configure_add_domain', 'wp-admin-bar-blog-settings' : 'my_sites_configure_settings', 'wp-admin-bar-legacy-dashboard' : 'my_sites_configure_wp_admin', // reader 'wp-admin-bar-followed-sites' : 'reader_followed_sites', 'wp-admin-bar-reader-followed-sites-manage': 'reader_manage_followed_sites', 'wp-admin-bar-discover-discover' : 'reader_discover', 'wp-admin-bar-discover-search' : 'reader_search', 'wp-admin-bar-my-activity-my-likes' : 'reader_my_likes', // account 'wp-admin-bar-user-info' : 'my_account_user_name', // account - profile 'wp-admin-bar-my-profile' : 'my_account_profile_my_profile', 'wp-admin-bar-account-settings' : 'my_account_profile_account_settings', 'wp-admin-bar-billing' : 'my_account_profile_manage_purchases', 'wp-admin-bar-security' : 'my_account_profile_security', 'wp-admin-bar-notifications' : 'my_account_profile_notifications', // account - special 'wp-admin-bar-get-apps' : 'my_account_special_get_apps', 'wp-admin-bar-next-steps' : 'my_account_special_next_steps', 'wp-admin-bar-help' : 'my_account_special_help', }; var notesTracksEvents = { openSite: function ( data ) { return { clicked: 'masterbar_notifications_panel_site', site_id: data.siteId }; }, openPost: function ( data ) { return { clicked: 'masterbar_notifications_panel_post', site_id: data.siteId, post_id: data.postId }; }, openComment: function ( data ) { return { clicked: 'masterbar_notifications_panel_comment', site_id: data.siteId, post_id: data.postId, comment_id: data.commentId }; } }; // Element.prototype.matches as a standalone function, with old browser fallback function matches( node, selector ) { if ( ! node ) { return undefined; } if ( ! Element.prototype.matches && ! Element.prototype.msMatchesSelector ) { throw new Error( 'Unsupported browser' ); } return Element.prototype.matches ? node.matches( selector ) : node.msMatchesSelector( selector ); } // Element.prototype.closest as a standalone function, with old browser fallback function closest( node, selector ) { if ( ! node ) { return undefined; } if ( Element.prototype.closest ) { return node.closest( selector ); } do { if ( matches( node, selector ) ) { return node; } node = node.parentElement || node.parentNode; } while ( node !== null && node.nodeType === 1 ); return null; } function recordTracksEvent( eventProps ) { eventProps = eventProps || {}; window._tkq = window._tkq || []; window._tkq.push( [ 'recordEvent', eventName, eventProps ] ); } function parseJson( s, defaultValue ) { try { return JSON.parse( s ); } catch ( e ) { return defaultValue; } } function createTrackableLinkEventHandler( link ) { return function () { var parent = closest( link, 'li' ); if ( ! parent ) { return; } var trackingId = link.getAttribute( 'ID' ) || parent.getAttribute( 'ID' ); if ( ! linksTracksEvents.hasOwnProperty( trackingId ) ) { return; } var eventProps = { 'clicked': linksTracksEvents[ trackingId ] }; recordTracksEvent( eventProps ); } } function init() { var trackableLinkSelector = '.mb-trackable .ab-item:not(div),' + '#wp-admin-bar-notes .ab-item,' + '#wp-admin-bar-user-info .ab-item,' + '.mb-trackable .ab-secondary'; var trackableLinks = document.querySelectorAll( trackableLinkSelector ); for ( var i = 0; i < trackableLinks.length; i++ ) { var link = trackableLinks[ i ]; var handler = createTrackableLinkEventHandler( link ); link.addEventListener( 'click', handler ); link.addEventListener( 'touchstart', handler ); } } if ( document.readyState === 'loading' ) { document.addEventListener( 'DOMContentLoaded', init ); } else { init(); } // listen for postMessage events from the notifications iframe window.addEventListener( 'message', function ( event ) { if ( event.origin !== 'https://web.archive.org/web/20211231085626/https://widgets.wp.com' ) { return; } var data = ( typeof event.data === 'string' ) ? parseJson( event.data, {} ) : event.data; if ( data.type !== 'notesIframeMessage' ) { return; } var eventData = notesTracksEvents[ data.action ]; if ( ! eventData ) { return; } recordTracksEvent( eventData( data ) ); }, false ); } )(); ; /*! This file is auto-generated */ !function(c,d){"use strict";var e=!1,n=!1;if(d.querySelector)if(c.addEventListener)e=!0;if(c.wp=c.wp||{},!c.wp.receiveEmbedMessage)if(c.wp.receiveEmbedMessage=function(e){var t=e.data;if(t)if(t.secret||t.message||t.value)if(!/[^a-zA-Z0-9]/.test(t.secret)){for(var r,a,i,s=d.querySelectorAll('iframe[data-secret="'+t.secret+'"]'),n=d.querySelectorAll('blockquote[data-secret="'+t.secret+'"]'),o=0;o<n.length;o++)n[o].style.display="none";for(o=0;o<s.length;o++)if(r=s[o],e.source===r.contentWindow){if(r.removeAttribute("style"),"height"===t.message){if(1e3<(i=parseInt(t.value,10)))i=1e3;else if(~~i<200)i=200;r.height=i}if("link"===t.message)if(a=d.createElement("a"),i=d.createElement("a"),a.href=r.getAttribute("src"),i.href=t.value,i.host===a.host)if(d.activeElement===r)c.top.location.href=t.value}}},e)c.addEventListener("message",c.wp.receiveEmbedMessage,!1),d.addEventListener("DOMContentLoaded",t,!1),c.addEventListener("load",t,!1);function t(){if(!n){n=!0;for(var e,t,r=-1!==navigator.appVersion.indexOf("MSIE 10"),a=!!navigator.userAgent.match(/Trident.*rv:11\./),i=d.querySelectorAll("iframe.wp-embedded-content"),s=0;s<i.length;s++){if(!(e=i[s]).getAttribute("data-secret"))t=Math.random().toString(36).substr(2,10),e.src+="#?secret="+t,e.setAttribute("data-secret",t);if(r||a)(t=e.cloneNode(!0)).removeAttribute("security"),e.parentNode.replaceChild(t,e)}}}}(window,document);; /* global wpcom, jetpackCarouselStrings, DocumentTouch */ ( function () { 'use strict'; var swiper; ///////////////////////////////////// // Utility functions ///////////////////////////////////// var util = ( function () { var noop = function () {}; function texturize( text ) { // Ensure we get a string. text = text + ''; text = text.replace( /'/g, '’' ).replace( /'/g, '’' ); text = text .replace( /"/g, '”' ) .replace( /"/g, '”' ) .replace( /"/g, '”' ) .replace( /[\u201D]/g, '”' ); // Untexturize allowed HTML tags params double-quotes. text = text.replace( /([\w]+)=&#[\d]+;(.+?)&#[\d]+;/g, '$1="$2"' ); return text.trim(); } function applyReplacements( text, replacements ) { if ( ! text ) { return; } if ( ! replacements ) { return text; } return text.replace( /{(\d+)}/g, function ( match, number ) { return typeof replacements[ number ] !== 'undefined' ? replacements[ number ] : match; } ); } function getBackgroundImage( imgEl ) { var canvas = document.createElement( 'canvas' ), context = canvas.getContext && canvas.getContext( '2d' ); if ( ! imgEl ) { return; } context.filter = 'blur(20px) '; context.drawImage( imgEl, 0, 0 ); var url = canvas.toDataURL( 'image/png' ); canvas = null; return url; } return { noop: noop, texturize: texturize, applyReplacements: applyReplacements, getBackgroundImage: getBackgroundImage, }; } )(); ///////////////////////////////////// // DOM-related utility functions ///////////////////////////////////// var domUtil = ( function () { // Helper matches function (not a polyfill), compatible with IE 11. function matches( el, sel ) { if ( Element.prototype.matches ) { return el.matches( sel ); } if ( Element.prototype.msMatchesSelector ) { return el.msMatchesSelector( sel ); } } // Helper closest parent node function (not a polyfill) based on // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill function closest( el, sel ) { if ( el.closest ) { return el.closest( sel ); } var current = el; do { if ( matches( current, sel ) ) { return current; } current = current.parentElement || current.parentNode; } while ( current !== null && current.nodeType === 1 ); return null; } function hide( el ) { if ( el ) { el.style.display = 'none'; } } function show( el ) { if ( el ) { // Everything we show and hide in Carousel is currently a block, // so we can make this really straightforward. el.style.display = 'block'; } } function fade( el, start, end, callback ) { if ( ! el ) { return callback(); } // Prepare for transition. // Ensure the item is in the render tree, in its initial state. el.style.removeProperty( 'display' ); el.style.opacity = start; el.style.transition = 'opacity 0.2s'; el.style.pointerEvents = 'none'; var finished = function ( e ) { if ( e.target === el && e.propertyName === 'opacity' ) { el.style.removeProperty( 'transition' ); el.style.removeProperty( 'opacity' ); el.style.removeProperty( 'pointer-events' ); el.removeEventListener( 'transitionend', finished ); el.removeEventListener( 'transitioncancel', finished ); callback(); } }; requestAnimationFrame( function () { // Double rAF for browser compatibility. requestAnimationFrame( function () { el.addEventListener( 'transitionend', finished ); el.addEventListener( 'transitioncancel', finished ); // Trigger transition. el.style.opacity = end; } ); } ); } function fadeIn( el, callback ) { callback = callback || util.noop; fade( el, '0', '1', callback ); } function fadeOut( el, callback ) { callback = callback || util.noop; fade( el, '1', '0', function () { if ( el ) { el.style.display = 'none'; } callback(); } ); } function emitEvent( el, type, detail ) { var e; try { e = new CustomEvent( type, { bubbles: true, cancelable: true, detail: detail || null, } ); } catch ( err ) { e = document.createEvent( 'CustomEvent' ); e.initCustomEvent( type, true, true, detail || null ); } el.dispatchEvent( e ); } // From: https://easings.net/#easeInOutQuad function easeInOutQuad( num ) { return num < 0.5 ? 2 * num * num : 1 - Math.pow( -2 * num + 2, 2 ) / 2; } function getFooterClearance( container ) { var footer = container.querySelector( '.jp-carousel-info-footer' ); var infoArea = container.querySelector( '.jp-carousel-info-extra' ); var contentArea = container.querySelector( '.jp-carousel-info-content-wrapper' ); if ( footer && infoArea && contentArea ) { var styles = window.getComputedStyle( infoArea ); var padding = parseInt( styles.paddingTop, 10 ) + parseInt( styles.paddingBottom, 10 ); padding = isNaN( padding ) ? 0 : padding; return contentArea.offsetHeight + footer.offsetHeight + padding; } return 0; } function isTouch() { return ( 'ontouchstart' in window || ( window.DocumentTouch && document instanceof DocumentTouch ) ); } function scrollToElement( el, container, callback ) { if ( ! el || ! container ) { if ( callback ) { return callback(); } return; } // For iOS Safari compatibility, use JS to set the minimum height. var infoArea = container.querySelector( '.jp-carousel-info-extra' ); if ( infoArea ) { // 64px is the same height as `.jp-carousel-info-footer` in the CSS. infoArea.style.minHeight = window.innerHeight - 64 + 'px'; } var isScrolling = true; var startTime = Date.now(); var duration = 300; var originalPosition = container.scrollTop; var targetPosition = Math.max( 0, el.offsetTop - Math.max( 0, window.innerHeight - getFooterClearance( container ) ) ); var distance = targetPosition - container.scrollTop; distance = Math.min( distance, container.scrollHeight - window.innerHeight ); function stopScroll() { isScrolling = false; } function runScroll() { var now = Date.now(); var progress = easeInOutQuad( ( now - startTime ) / duration ); progress = progress > 1 ? 1 : progress; var newVal = progress * distance; container.scrollTop = originalPosition + newVal; if ( now <= startTime + duration && isScrolling ) { return requestAnimationFrame( runScroll ); } if ( callback ) { callback(); } if ( infoArea ) { infoArea.style.minHeight = ''; } isScrolling = false; container.removeEventListener( 'wheel', stopScroll ); } // Allow scroll to be cancelled by user interaction. container.addEventListener( 'wheel', stopScroll ); runScroll(); } function getJSONAttribute( el, attr ) { if ( ! el || ! el.hasAttribute( attr ) ) { return undefined; } try { return JSON.parse( el.getAttribute( attr ) ); } catch ( e ) { return undefined; } } function convertToPlainText( html ) { var dummy = document.createElement( 'div' ); dummy.textContent = html; return dummy.innerHTML; } function stripHTML( text ) { return text.replace( /<[^>]*>?/gm, '' ); } return { closest: closest, matches: matches, hide: hide, show: show, fadeIn: fadeIn, fadeOut: fadeOut, scrollToElement: scrollToElement, getJSONAttribute: getJSONAttribute, convertToPlainText: convertToPlainText, stripHTML: stripHTML, emitEvent: emitEvent, isTouch: isTouch, }; } )(); ///////////////////////////////////// // Carousel implementation ///////////////////////////////////// function init() { var commentInterval; var screenPadding; var originalOverflow; var originalHOverflow; var scrollPos; var lastKnownLocationHash = ''; var isUserTyping = false; var gallerySelector = 'div.gallery, div.tiled-gallery, ul.wp-block-gallery, ul.blocks-gallery-grid, ' + 'figure.wp-block-gallery.has-nested-images, div.wp-block-jetpack-tiled-gallery, a.single-image-gallery'; // Selector for items within a gallery or tiled gallery. var galleryItemSelector = '.gallery-item, .tiled-gallery-item, .blocks-gallery-item, ' + ' .tiled-gallery__item'; // Selector for all items including single images. var itemSelector = galleryItemSelector + ', .wp-block-image'; var carousel = {}; var stat = typeof wpcom !== 'undefined' && wpcom.carousel && wpcom.carousel.stat ? wpcom.carousel.stat : util.noop; var pageview = typeof wpcom !== 'undefined' && wpcom.carousel && wpcom.carousel.pageview ? wpcom.carousel.pageview : util.noop; function handleKeyboardEvent( e ) { if ( ! isUserTyping ) { switch ( e.which ) { case 38: // up e.preventDefault(); carousel.overlay.scrollTop -= 100; break; case 40: // down e.preventDefault(); carousel.overlay.scrollTop += 100; break; case 39: // right e.preventDefault(); swiper.slideNext(); break; case 37: // left case 8: // backspace e.preventDefault(); swiper.slidePrev(); break; case 27: // escape e.preventDefault(); closeCarousel(); break; default: break; } } } function disableKeyboardNavigation() { isUserTyping = true; } function enableKeyboardNavigation() { isUserTyping = false; } function calculatePadding() { var baseScreenPadding = 110; screenPadding = baseScreenPadding; if ( window.innerWidth <= 760 ) { screenPadding = Math.round( ( window.innerWidth / 760 ) * baseScreenPadding ); if ( screenPadding < 40 && domUtil.isTouch() ) { screenPadding = 0; } } } function initializeCarousel() { if ( ! carousel.overlay ) { carousel.overlay = document.querySelector( '.jp-carousel-overlay' ); carousel.container = carousel.overlay.querySelector( '.jp-carousel-wrap' ); carousel.gallery = carousel.container.querySelector( '.jp-carousel' ); carousel.info = carousel.overlay.querySelector( '.jp-carousel-info' ); carousel.caption = carousel.info.querySelector( '.jp-carousel-caption' ); carousel.commentField = carousel.overlay.querySelector( '#jp-carousel-comment-form-comment-field' ); carousel.emailField = carousel.overlay.querySelector( '#jp-carousel-comment-form-email-field' ); carousel.authorField = carousel.overlay.querySelector( '#jp-carousel-comment-form-author-field' ); carousel.urlField = carousel.overlay.querySelector( '#jp-carousel-comment-form-url-field' ); calculatePadding(); [ carousel.commentField, carousel.emailField, carousel.authorField, carousel.urlField, ].forEach( function ( field ) { if ( field ) { field.addEventListener( 'focus', disableKeyboardNavigation ); field.addEventListener( 'blur', enableKeyboardNavigation ); } } ); carousel.overlay.addEventListener( 'click', function ( e ) { var target = e.target; var isTargetCloseHint = !! domUtil.closest( target, '.jp-carousel-close-hint' ); var isSmallScreen = !! window.matchMedia( '(max-device-width: 760px)' ).matches; if ( target === carousel.overlay ) { if ( isSmallScreen ) { return; } else { closeCarousel(); } } else if ( isTargetCloseHint ) { closeCarousel(); } else if ( target.classList.contains( 'jp-carousel-image-download' ) ) { stat( 'download_original_click' ); } else if ( target.classList.contains( 'jp-carousel-comment-login' ) ) { handleCommentLoginClick( e ); } else if ( domUtil.closest( target, '#jp-carousel-comment-form-container' ) ) { handleCommentFormClick( e ); } else if ( domUtil.closest( target, '.jp-carousel-photo-icons-container' ) || target.classList.contains( 'jp-carousel-photo-title' ) ) { handleFooterElementClick( e ); } else if ( ! domUtil.closest( target, '.jp-carousel-info' ) ) { return; } } ); window.addEventListener( 'keydown', handleKeyboardEvent ); carousel.overlay.addEventListener( 'jp_carousel.afterOpen', function () { enableKeyboardNavigation(); // Don't show navigation if there's only one image. if ( carousel.slides.length <= 1 ) { return; } // Show dot pagination if slide count is <= 5, otherwise show n/total. if ( carousel.slides.length <= 5 ) { domUtil.show( carousel.info.querySelector( '.jp-swiper-pagination' ) ); } else { domUtil.show( carousel.info.querySelector( '.jp-carousel-pagination' ) ); } } ); carousel.overlay.addEventListener( 'jp_carousel.beforeClose', function () { disableKeyboardNavigation(); // Fixes some themes where closing carousel brings view back to top. document.documentElement.style.removeProperty( 'height' ); // If we disable the swiper (because there's only one image) // we have to re-enable it here again as Swiper doesn't, for some reason, // show the navigation buttons again after reinitialization. if ( swiper ) { swiper.enable(); } // Hide pagination. domUtil.hide( carousel.info.querySelector( '.jp-swiper-pagination' ) ); domUtil.hide( carousel.info.querySelector( '.jp-carousel-pagination' ) ); } ); carousel.overlay.addEventListener( 'jp_carousel.afterClose', function () { // don't force the browser back when the carousel closes. if ( window.history.pushState ) { history.pushState( '', document.title, window.location.pathname + window.location.search ); } else { window.location.href = ''; } lastKnownLocationHash = ''; carousel.isOpen = false; } ); // Prevent native browser zooming carousel.overlay.addEventListener( 'touchstart', function ( e ) { if ( e.touches.length > 1 ) { e.preventDefault(); } } ); } } function handleCommentLoginClick() { var slide = carousel.currentSlide; var attachmentId = slide ? slide.attrs.attachmentId : '0'; window.location.href = jetpackCarouselStrings.login_url + '%23jp-carousel-' + attachmentId; } function updatePostResults( msg, isSuccess ) { var results = carousel.overlay.querySelector( '#jp-carousel-comment-post-results' ); var elClass = 'jp-carousel-comment-post-' + ( isSuccess ? 'success' : 'error' ); results.innerHTML = '<span class="' + elClass + '">' + msg + '</span>'; domUtil.hide( carousel.overlay.querySelector( '#jp-carousel-comment-form-spinner' ) ); carousel.overlay .querySelector( '#jp-carousel-comment-form' ) .classList.remove( 'jp-carousel-is-disabled' ); domUtil.show( results ); } function handleCommentFormClick( e ) { var target = e.target; var data = domUtil.getJSONAttribute( carousel.container, 'data-carousel-extra' ) || {}; var attachmentId = carousel.currentSlide.attrs.attachmentId; var wrapper = document.querySelector( '#jp-carousel-comment-form-submit-and-info-wrapper' ); var spinner = document.querySelector( '#jp-carousel-comment-form-spinner' ); var submit = document.querySelector( '#jp-carousel-comment-form-button-submit' ); var form = document.querySelector( '#jp-carousel-comment-form' ); if ( carousel.commentField && carousel.commentField.getAttribute( 'id' ) === target.getAttribute( 'id' ) ) { // For first page load disableKeyboardNavigation(); domUtil.show( wrapper ); } else if ( domUtil.matches( target, 'input[type="submit"]' ) ) { e.preventDefault(); e.stopPropagation(); domUtil.show( spinner ); form.classList.add( 'jp-carousel-is-disabled' ); var ajaxData = { action: 'post_attachment_comment', nonce: jetpackCarouselStrings.nonce, blog_id: data.blog_id, id: attachmentId, comment: carousel.commentField.value, }; if ( ! ajaxData.comment.length ) { updatePostResults( jetpackCarouselStrings.no_comment_text, false ); return; } if ( Number( jetpackCarouselStrings.is_logged_in ) !== 1 ) { ajaxData.email = carousel.emailField.value; ajaxData.author = carousel.authorField.value; ajaxData.url = carousel.urlField.value; if ( Number( jetpackCarouselStrings.require_name_email ) === 1 ) { if ( ! ajaxData.email.length || ! ajaxData.email.match( '@' ) ) { updatePostResults( jetpackCarouselStrings.no_comment_email, false ); return; } else if ( ! ajaxData.author.length ) { updatePostResults( jetpackCarouselStrings.no_comment_author, false ); return; } } } var xhr = new XMLHttpRequest(); xhr.open( 'POST', jetpackCarouselStrings.ajaxurl, true ); xhr.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' ); xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8' ); xhr.onreadystatechange = function () { if ( this.readyState === XMLHttpRequest.DONE && this.status >= 200 && this.status < 300 ) { var response; try { response = JSON.parse( this.response ); } catch ( error ) { updatePostResults( jetpackCarouselStrings.comment_post_error, false ); return; } if ( response.comment_status === 'approved' ) { updatePostResults( jetpackCarouselStrings.comment_approved, true ); } else if ( response.comment_status === 'unapproved' ) { updatePostResults( jetpackCarouselStrings.comment_unapproved, true ); } else { // 'deleted', 'spam', false updatePostResults( jetpackCarouselStrings.comment_post_error, false ); } clearCommentTextAreaValue(); fetchComments( attachmentId ); submit.value = jetpackCarouselStrings.post_comment; domUtil.hide( spinner ); form.classList.remove( 'jp-carousel-is-disabled' ); } else { // TODO: Add error handling and display here updatePostResults( jetpackCarouselStrings.comment_post_error, false ); } }; var params = []; for ( var item in ajaxData ) { if ( item ) { // Encode each form element into a URI-compatible string. var encoded = encodeURIComponent( item ) + '=' + encodeURIComponent( ajaxData[ item ] ); // In x-www-form-urlencoded, spaces should be `+`, not `%20`. params.push( encoded.replace( /%20/g, '+' ) ); } } var encodedData = params.join( '&' ); xhr.send( encodedData ); } } /** * Handles clicks to icons and other action elements in the icon container. * @param {MouseEvent|TouchEvent|KeyBoardEvent} Event object. */ function handleFooterElementClick( e ) { e.preventDefault(); var target = e.target; var extraInfoContainer = carousel.info.querySelector( '.jp-carousel-info-extra' ); var photoMetaContainer = carousel.info.querySelector( '.jp-carousel-image-meta' ); var commentsContainer = carousel.info.querySelector( '.jp-carousel-comments-wrapper' ); var infoIcon = carousel.info.querySelector( '.jp-carousel-icon-info' ); var commentsIcon = carousel.info.querySelector( '.jp-carousel-icon-comments' ); function handleInfoToggle() { if ( commentsIcon ) { commentsIcon.classList.remove( 'jp-carousel-selected' ); } infoIcon.classList.toggle( 'jp-carousel-selected' ); if ( commentsContainer ) { commentsContainer.classList.remove( 'jp-carousel-show' ); } if ( photoMetaContainer ) { photoMetaContainer.classList.toggle( 'jp-carousel-show' ); if ( photoMetaContainer.classList.contains( 'jp-carousel-show' ) ) { extraInfoContainer.classList.add( 'jp-carousel-show' ); } else { extraInfoContainer.classList.remove( 'jp-carousel-show' ); } } } function handleCommentToggle() { if ( infoIcon ) { infoIcon.classList.remove( 'jp-carousel-selected' ); } commentsIcon.classList.toggle( 'jp-carousel-selected' ); if ( photoMetaContainer ) { photoMetaContainer.classList.remove( 'jp-carousel-show' ); } if ( commentsContainer ) { commentsContainer.classList.toggle( 'jp-carousel-show' ); if ( commentsContainer.classList.contains( 'jp-carousel-show' ) ) { extraInfoContainer.classList.add( 'jp-carousel-show' ); } else { extraInfoContainer.classList.remove( 'jp-carousel-show' ); } } } if ( domUtil.closest( target, '.jp-carousel-icon-info' ) || target.classList.contains( 'jp-carousel-photo-title' ) ) { if ( photoMetaContainer && photoMetaContainer.classList.contains( 'jp-carousel-show' ) ) { domUtil.scrollToElement( carousel.overlay, carousel.overlay, handleInfoToggle ); } else { handleInfoToggle(); domUtil.scrollToElement( carousel.info, carousel.overlay ); } } if ( domUtil.closest( target, '.jp-carousel-icon-comments' ) ) { if ( commentsContainer && commentsContainer.classList.contains( 'jp-carousel-show' ) ) { domUtil.scrollToElement( carousel.overlay, carousel.overlay, handleCommentToggle ); } else { handleCommentToggle(); domUtil.scrollToElement( carousel.info, carousel.overlay ); } } } function processSingleImageGallery() { var images = document.querySelectorAll( 'a img[data-attachment-id]' ); Array.prototype.forEach.call( images, function ( image ) { var link = image.parentElement; var container = link.parentElement; // Skip if image was already added to gallery by shortcode. if ( container.classList.contains( 'gallery-icon' ) ) { return; } // Skip if image is part of a gallery. if ( domUtil.closest( container, galleryItemSelector ) ) { return; } // Skip if the parent is not actually a link. if ( ! link.hasAttribute( 'href' ) ) { return; } var valid = false; // If link points to 'Media File' (ignoring GET parameters) and flag is set, allow it. if ( link.getAttribute( 'href' ).split( '?' )[ 0 ] === image.getAttribute( 'data-orig-file' ).split( '?' )[ 0 ] && Number( jetpackCarouselStrings.single_image_gallery_media_file ) === 1 ) { valid = true; } // If link points to 'Attachment Page', allow it. if ( link.getAttribute( 'href' ) === image.getAttribute( 'data-permalink' ) ) { valid = true; } // Links to 'Custom URL' or 'Media File' when flag is not set are not valid. if ( ! valid ) { return; } // Make this node a gallery recognizable by event listener above. link.classList.add( 'single-image-gallery' ); // blog_id is needed to allow posting comments to correct blog. link.setAttribute( 'data-carousel-extra', JSON.stringify( { blog_id: Number( jetpackCarouselStrings.blog_id ), } ) ); } ); } function testForData( el ) { return !! ( el && el.getAttribute( 'data-carousel-extra' ) ); } function openOrSelectSlide( gal, index ) { if ( ! carousel.isOpen ) { // The `open` method selects the correct slide during the initialization. loadSwiper( gal, { startIndex: index } ); } else { selectSlideAtIndex( index ); // We have to force swiper to slide to the index onHasChange. swiper.slideTo( index + 1 ); } } function selectSlideAtIndex( index ) { if ( ! index || index < 0 || index > carousel.slides.length ) { index = 0; } carousel.currentSlide = carousel.slides[ index ]; var current = carousel.currentSlide; var attachmentId = current.attrs.attachmentId; var infoIcon = carousel.info.querySelector( '.jp-carousel-icon-info' ); var commentsIcon = carousel.info.querySelector( '.jp-carousel-icon-comments' ); // If the comment/info section is toggled open, it's kept open, but scroll to top of the next slide. if ( ( infoIcon && infoIcon.classList.contains( 'jp-carousel-selected' ) ) || ( commentsIcon && commentsIcon.classList.contains( 'jp-carousel-selected' ) ) ) { if ( carousel.overlay.scrollTop !== 0 ) { domUtil.scrollToElement( carousel.overlay, carousel.overlay ); } } loadFullImage( carousel.slides[ index ] ); if ( Number( jetpackCarouselStrings.display_background_image ) === 1 && ! carousel.slides[ index ].backgroundImage ) { loadBackgroundImage( carousel.slides[ index ] ); } domUtil.hide( carousel.caption ); updateTitleCaptionAndDesc( { caption: current.attrs.caption, title: current.attrs.title, desc: current.attrs.desc, } ); var imageMeta = carousel.slides[ index ].attrs.imageMeta; updateExif( imageMeta ); updateFullSizeLink( current ); if ( Number( jetpackCarouselStrings.display_comments ) === 1 ) { testCommentsOpened( carousel.slides[ index ].attrs.commentsOpened ); fetchComments( attachmentId ); domUtil.hide( carousel.info.querySelector( '#jp-carousel-comment-post-results' ) ); } // Update pagination in footer. var pagination = carousel.info.querySelector( '.jp-carousel-pagination' ); if ( pagination && carousel.slides.length > 5 ) { var currentPage = index + 1; pagination.innerHTML = '<span>' + currentPage + ' / ' + carousel.slides.length + '</span>'; } // Record pageview in WP Stats, for each new image loaded full-screen. if ( jetpackCarouselStrings.stats ) { new Image().src = document.location.protocol + '//web.archive.org/web/20211231085626/https://pixel.wp.com/g.gif?' + jetpackCarouselStrings.stats + '&post=' + encodeURIComponent( attachmentId ) + '&rand=' + Math.random(); } pageview( attachmentId ); window.location.hash = lastKnownLocationHash = '#jp-carousel-' + attachmentId; } function restoreScroll() { window.scrollTo( window.scrollX || window.pageXOffset || 0, scrollPos || 0 ); } function closeCarousel() { // Make sure to let the page scroll again. document.body.style.overflow = originalOverflow; document.documentElement.style.overflow = originalHOverflow; clearCommentTextAreaValue(); disableKeyboardNavigation(); domUtil.emitEvent( carousel.overlay, 'jp_carousel.beforeClose' ); restoreScroll(); swiper.destroy(); carousel.isOpen = false; // Clear slide data for DOM garbage collection. carousel.slides = []; carousel.currentSlide = undefined; carousel.gallery.innerHTML = ''; domUtil.fadeOut( carousel.overlay, function () { domUtil.emitEvent( carousel.overlay, 'jp_carousel.afterClose' ); } ); } function calculateMaxSlideDimensions() { return { width: window.innerWidth, height: window.innerHeight - 64, //subtract height of bottom info bar, }; } function selectBestImageUrl( args ) { if ( typeof args !== 'object' ) { args = {}; } if ( typeof args.origFile === 'undefined' ) { return ''; } if ( typeof args.origWidth === 'undefined' || typeof args.maxWidth === 'undefined' ) { return args.origFile; } if ( typeof args.mediumFile === 'undefined' || typeof args.largeFile === 'undefined' ) { return args.origFile; } // Check if the image is being served by Photon (using a regular expression on the hostname). var imageLinkParser = document.createElement( 'a' ); imageLinkParser.href = args.largeFile; var isPhotonUrl = /^i[0-2]\.wp\.com$/i.test( imageLinkParser.hostname ); var mediumSizeParts = getImageSizeParts( args.mediumFile, args.origWidth, isPhotonUrl ); var largeSizeParts = getImageSizeParts( args.largeFile, args.origWidth, isPhotonUrl ); var largeWidth = parseInt( largeSizeParts[ 0 ], 10 ); var largeHeight = parseInt( largeSizeParts[ 1 ], 10 ); var mediumWidth = parseInt( mediumSizeParts[ 0 ], 10 ); var mediumHeight = parseInt( mediumSizeParts[ 1 ], 10 ); args.origMaxWidth = args.maxWidth; args.origMaxHeight = args.maxHeight; // Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc) if ( typeof window.devicePixelRatio !== 'undefined' && window.devicePixelRatio > 1 ) { args.maxWidth = args.maxWidth * window.devicePixelRatio; args.maxHeight = args.maxHeight * window.devicePixelRatio; } if ( largeWidth >= args.maxWidth || largeHeight >= args.maxHeight ) { return args.largeFile; } if ( mediumWidth >= args.maxWidth || mediumHeight >= args.maxHeight ) { return args.mediumFile; } if ( isPhotonUrl ) { // args.origFile doesn't point to a Photon url, so in this case we use args.largeFile // to return the photon url of the original image. var largeFileIndex = args.largeFile.lastIndexOf( '?' ); var origPhotonUrl = args.largeFile; if ( largeFileIndex !== -1 ) { origPhotonUrl = args.largeFile.substring( 0, largeFileIndex ); // If we have a really large image load a smaller version // that is closer to the viewable size if ( args.origWidth > args.maxWidth || args.origHeight > args.maxHeight ) { // @2x the max sizes so we get a high enough resolution for zooming. args.origMaxWidth = args.maxWidth * 2; args.origMaxHeight = args.maxHeight * 2; origPhotonUrl += '?fit=' + args.origMaxWidth + '%2C' + args.origMaxHeight; } } return origPhotonUrl; } return args.origFile; } function getImageSizeParts( file, origWidth, isPhotonUrl ) { var size = isPhotonUrl ? file.replace( /.*=([\d]+%2C[\d]+).*$/, '$1' ) : file.replace( /.*-([\d]+x[\d]+)\..+$/, '$1' ); var sizeParts = size !== file ? isPhotonUrl ? size.split( '%2C' ) : size.split( 'x' ) : [ origWidth, 0 ]; // If one of the dimensions is set to 9999, then the actual value of that dimension can't be retrieved from the url. // In that case, we set the value to 0. if ( sizeParts[ 0 ] === '9999' ) { sizeParts[ 0 ] = '0'; } if ( sizeParts[ 1 ] === '9999' ) { sizeParts[ 1 ] = '0'; } return sizeParts; } /** * Returns a number in a fraction format that represents the shutter speed. * @param Number speed * @return String */ function formatShutterSpeed( speed ) { var denominator; // round to one decimal if value > 1s by multiplying it by 10, rounding, then dividing by 10 again if ( speed >= 1 ) { return Math.round( speed * 10 ) / 10 + 's'; } // If the speed is less than one, we find the denominator by inverting // the number. Since cameras usually use rational numbers as shutter // speeds, we should get a nice round number. Or close to one in cases // like 1/30. So we round it. denominator = Math.round( 1 / speed ); return '1/' + denominator + 's'; } function parseTitleOrDesc( value ) { if ( ! value.match( ' ' ) && value.match( '_' ) ) { return ''; } return value; } function updateTitleCaptionAndDesc( data ) { var caption = ''; var title = ''; var desc = ''; var captionMainElement; var captionInfoExtraElement; var titleElement; var descriptionElement; captionMainElement = carousel.overlay.querySelector( '.jp-carousel-photo-caption' ); captionInfoExtraElement = carousel.overlay.querySelector( '.jp-carousel-caption' ); titleElement = carousel.overlay.querySelector( '.jp-carousel-photo-title' ); descriptionElement = carousel.overlay.querySelector( '.jp-carousel-photo-description' ); domUtil.hide( captionMainElement ); domUtil.hide( captionInfoExtraElement ); domUtil.hide( titleElement ); domUtil.hide( descriptionElement ); caption = parseTitleOrDesc( data.caption ) || ''; title = parseTitleOrDesc( data.title ) || ''; desc = parseTitleOrDesc( data.desc ) || ''; if ( caption || title || desc ) { if ( caption ) { captionMainElement.innerHTML = caption; captionInfoExtraElement.innerHTML = caption; domUtil.show( captionMainElement ); domUtil.show( captionInfoExtraElement ); } if ( domUtil.stripHTML( caption ) === domUtil.stripHTML( title ) ) { title = ''; } if ( domUtil.stripHTML( caption ) === domUtil.stripHTML( desc ) ) { desc = ''; } if ( domUtil.stripHTML( title ) === domUtil.stripHTML( desc ) ) { desc = ''; } if ( desc ) { descriptionElement.innerHTML = desc; domUtil.show( descriptionElement ); if ( ! title && ! caption ) { captionMainElement.innerHTML = domUtil.stripHTML( desc ); domUtil.show( captionMainElement ); } } if ( title ) { var plainTitle = domUtil.stripHTML( title ); titleElement.innerHTML = plainTitle; if ( ! caption ) { captionMainElement.innerHTML = plainTitle; captionInfoExtraElement.innerHTML = plainTitle; domUtil.show( captionMainElement ); } domUtil.show( titleElement ); } } } // updateExif updates the contents of the exif UL (.jp-carousel-image-exif) function updateExif( meta ) { if ( ! meta || Number( jetpackCarouselStrings.display_exif ) !== 1 ) { return false; } var ul = carousel.info.querySelector( '.jp-carousel-image-meta ul.jp-carousel-image-exif' ); var html = ''; for ( var key in meta ) { var val = meta[ key ]; var metaKeys = jetpackCarouselStrings.meta_data || []; if ( parseFloat( val ) === 0 || ! val.length || metaKeys.indexOf( key ) === -1 ) { continue; } switch ( key ) { case 'focal_length': val = val + 'mm'; break; case 'shutter_speed': val = formatShutterSpeed( val ); break; case 'aperture': val = 'f/' + val; break; } html += '<li><h5>' + jetpackCarouselStrings[ key ] + '</h5>' + val + '</li>'; } ul.innerHTML = html; ul.style.removeProperty( 'display' ); } // Update the contents of the jp-carousel-image-download link function updateFullSizeLink( currentSlide ) { if ( ! currentSlide ) { return false; } var original; var origSize = [ currentSlide.attrs.origWidth, currentSlide.attrs.origHeight ]; var imageLinkParser = document.createElement( 'a' ); imageLinkParser.href = currentSlide.attrs.src.replace( /\?.+$/, '' ); // Is this a Photon URL? if ( imageLinkParser.hostname.match( /^i[\d]{1}\.wp\.com$/i ) !== null ) { original = imageLinkParser.href; } else { original = currentSlide.attrs.origFile.replace( /\?.+$/, '' ); } var downloadText = carousel.info.querySelector( '.jp-carousel-download-text' ); var permalink = carousel.info.querySelector( '.jp-carousel-image-download' ); downloadText.innerHTML = util.applyReplacements( jetpackCarouselStrings.download_original, origSize ); permalink.setAttribute( 'href', original ); permalink.style.removeProperty( 'display' ); } function testCommentsOpened( opened ) { var commentForm = carousel.container.querySelector( '.jp-carousel-comment-form-container' ); var isOpened = parseInt( opened, 10 ) === 1; if ( isOpened ) { domUtil.fadeIn( commentForm ); } else { domUtil.fadeOut( commentForm ); } } function fetchComments( attachmentId, offset ) { var shouldClear = offset === undefined; var commentsIndicator = carousel.info.querySelector( '.jp-carousel-icon-comments .jp-carousel-has-comments-indicator' ); commentsIndicator.classList.remove( 'jp-carousel-show' ); clearInterval( commentInterval ); if ( ! attachmentId ) { return; } if ( ! offset || offset < 1 ) { offset = 0; } var comments = carousel.info.querySelector( '.jp-carousel-comments' ); var commentsLoading = carousel.info.querySelector( '#jp-carousel-comments-loading' ); domUtil.show( commentsLoading ); if ( shouldClear ) { domUtil.hide( comments ); comments.innerHTML = ''; } var xhr = new XMLHttpRequest(); var url = jetpackCarouselStrings.ajaxurl + '?action=get_attachment_comments' + '&nonce=' + jetpackCarouselStrings.nonce + '&id=' + attachmentId + '&offset=' + offset; xhr.open( 'GET', url ); xhr.setRequestHeader( 'X-Requested-With', 'XMLHttpRequest' ); var onError = function () { domUtil.fadeIn( comments ); domUtil.fadeOut( commentsLoading ); }; xhr.onload = function () { // Ignore the results if they arrive late and we're now on a different slide. if ( ! carousel.currentSlide || carousel.currentSlide.attrs.attachmentId !== attachmentId ) { return; } var isSuccess = xhr.status >= 200 && xhr.status < 300; var data; try { data = JSON.parse( xhr.responseText ); } catch ( e ) { // Do nothing. } if ( ! isSuccess || ! data || ! Array.isArray( data ) ) { return onError(); } if ( shouldClear ) { comments.innerHTML = ''; } for ( var i = 0; i < data.length; i++ ) { var entry = data[ i ]; var comment = document.createElement( 'div' ); comment.classList.add( 'jp-carousel-comment' ); comment.setAttribute( 'id', 'jp-carousel-comment-' + entry.id ); comment.innerHTML = '<div class="comment-gravatar">' + entry.gravatar_markup + '</div>' + '<div class="comment-content">' + '<div class="comment-author">' + entry.author_markup + '</div>' + '<div class="comment-date">' + entry.date_gmt + '</div>' + entry.content + '</div>'; comments.appendChild( comment ); // Set the interval to check for a new page of comments. clearInterval( commentInterval ); commentInterval = setInterval( function () { if ( carousel.container.scrollTop + 150 > window.innerHeight ) { fetchComments( attachmentId, offset + 10 ); clearInterval( commentInterval ); } }, 300 ); } if ( data.length > 0 ) { domUtil.show( comments ); commentsIndicator.innerText = data.length; commentsIndicator.classList.add( 'jp-carousel-show' ); } domUtil.hide( commentsLoading ); }; xhr.onerror = onError; xhr.send(); } function loadFullImage( slide ) { var el = slide.el; var attrs = slide.attrs; var image = el.querySelector( 'img' ); if ( ! image.hasAttribute( 'data-loaded' ) ) { var hasPreview = !! attrs.previewImage; var thumbSize = attrs.thumbSize; if ( ! hasPreview || ( thumbSize && el.offsetWidth > thumbSize.width ) ) { image.src = attrs.src; } else { image.src = attrs.previewImage; } image.setAttribute( 'itemprop', 'image' ); image.setAttribute( 'data-loaded', 1 ); } } function loadBackgroundImage( slide ) { var currentSlide = slide.el; if ( swiper && swiper.slides ) { currentSlide = swiper.slides[ swiper.activeIndex ]; } var image = slide.attrs.originalElement; var isLoaded = image.complete && image.naturalHeight !== 0; if ( isLoaded ) { applyBackgroundImage( slide, currentSlide, image ); return; } image.onload = function () { applyBackgroundImage( slide, currentSlide, image ); }; } function applyBackgroundImage( slide, currentSlide, image ) { var url = util.getBackgroundImage( image ); slide.backgroundImage = url; currentSlide.style.backgroundImage = 'url(' + url + ')'; currentSlide.style.backgroundSize = 'cover'; } function clearCommentTextAreaValue() { if ( carousel.commentField ) { carousel.commentField.value = ''; } } function getOriginalDimensions( el ) { var size = el.getAttribute( 'data-orig-size' ) || ''; if ( size ) { var parts = size.split( ',' ); return { width: parseInt( parts[ 0 ], 10 ), height: parseInt( parts[ 1 ], 10 ) }; } else { return { width: el.getAttribute( 'data-original-width' ) || el.getAttribute( 'width' ) || undefined, height: el.getAttribute( 'data-original-height' ) || el.getAttribute( 'height' ) || undefined, }; } } function initCarouselSlides( items, startIndex ) { carousel.slides = []; var max = calculateMaxSlideDimensions(); // If the startIndex is not 0 then preload the clicked image first. if ( startIndex !== 0 ) { var img = new Image(); img.src = items[ startIndex ].getAttribute( 'data-gallery-src' ); } var useInPageThumbnails = !! domUtil.closest( items[ 0 ], '.tiled-gallery.type-rectangular' ); // create the 'slide' Array.prototype.forEach.call( items, function ( item, i ) { var permalinkEl = domUtil.closest( item, 'a' ); var origFile = item.getAttribute( 'data-orig-file' ) || item.getAttribute( 'src-orig' ); var attrID = item.getAttribute( 'data-attachment-id' ) || item.getAttribute( 'data-id' ) || '0'; var caption = document.querySelector( 'img[data-attachment-id="' + attrID + '"] + figcaption' ); if ( caption ) { caption = caption.innerHTML; } else { caption = item.getAttribute( 'data-image-caption' ); } var attrs = { originalElement: item, attachmentId: attrID, commentsOpened: item.getAttribute( 'data-comments-opened' ) || '0', imageMeta: domUtil.getJSONAttribute( item, 'data-image-meta' ) || {}, title: item.getAttribute( 'data-image-title' ) || '', desc: item.getAttribute( 'data-image-description' ) || '', mediumFile: item.getAttribute( 'data-medium-file' ) || '', largeFile: item.getAttribute( 'data-large-file' ) || '', origFile: origFile || '', thumbSize: { width: item.naturalWidth, height: item.naturalHeight }, caption: caption || '', permalink: permalinkEl && permalinkEl.getAttribute( 'href' ), src: origFile || item.getAttribute( 'src' ) || '', }; var tiledGalleryItem = domUtil.closest( item, '.tiled-gallery-item' ); var tiledCaptionEl = tiledGalleryItem && tiledGalleryItem.querySelector( '.tiled-gallery-caption' ); var tiledCaption = tiledCaptionEl && tiledCaptionEl.innerHTML; if ( tiledCaption ) { attrs.caption = tiledCaption; } var origDimensions = getOriginalDimensions( item ); attrs.origWidth = origDimensions.width || attrs.thumbSize.width; attrs.origHeight = origDimensions.height || attrs.thumbSize.height; if ( typeof wpcom !== 'undefined' && wpcom.carousel && wpcom.carousel.generateImgSrc ) { attrs.src = wpcom.carousel.generateImgSrc( item, max ); } else { attrs.src = selectBestImageUrl( { origFile: attrs.src, origWidth: attrs.origWidth, origHeight: attrs.origHeight, maxWidth: max.width, maxHeight: max.height, mediumFile: attrs.mediumFile, largeFile: attrs.largeFile, } ); } // Set the final src. item.setAttribute( 'data-gallery-src', attrs.src ); if ( attrs.attachmentId !== '0' ) { attrs.title = util.texturize( attrs.title ); attrs.desc = util.texturize( attrs.desc ); attrs.caption = util.texturize( attrs.caption ); // Initially, the image is a 1x1 transparent gif. // The preview is shown as a background image on the slide itself. var image = new Image(); image.src = attrs.src; var slideEl = document.createElement( 'div' ); slideEl.classList.add( 'swiper-slide' ); slideEl.setAttribute( 'itemprop', 'associatedMedia' ); slideEl.setAttribute( 'itemscope', '' ); slideEl.setAttribute( 'itemtype', 'https://web.archive.org/web/20211231085626/https://schema.org/ImageObject' ); var zoomEl = document.createElement( 'div' ); zoomEl.classList.add( 'swiper-zoom-container' ); carousel.gallery.appendChild( slideEl ); slideEl.appendChild( zoomEl ); zoomEl.appendChild( image ); slideEl.setAttribute( 'data-attachment-id', attrs.attachmentId ); slideEl.setAttribute( 'data-permalink', attrs.permalink ); slideEl.setAttribute( 'data-orig-file', attrs.origFile ); if ( useInPageThumbnails ) { // Use the image already loaded in the gallery as a preview. attrs.previewImage = attrs.src; } var slide = { el: slideEl, attrs: attrs, index: i }; carousel.slides.push( slide ); } } ); } function loadSwiper( gallery, options ) { if ( ! window.Swiper670 ) { var loader = document.querySelector( '#jp-carousel-loading-overlay' ); domUtil.show( loader ); var jsScript = document.createElement( 'script' ); jsScript.id = 'jetpack-carousel-swiper-js'; jsScript.src = window.jetpackSwiperLibraryPath.url; jsScript.async = true; jsScript.onload = function () { domUtil.hide( loader ); openCarousel( gallery, options ); }; jsScript.onerror = function () { domUtil.hide( loader ); }; document.head.appendChild( jsScript ); return; } openCarousel( gallery, options ); } function openCarousel( gallery, options ) { var settings = { imgSelector: '.gallery-item [data-attachment-id], .tiled-gallery-item [data-attachment-id], img[data-attachment-id], img[data-id]', startIndex: 0, }; var data = domUtil.getJSONAttribute( gallery, 'data-carousel-extra' ); var tapTimeout; if ( ! data ) { return; // don't run if the default gallery functions weren't used } initializeCarousel(); if ( carousel.isOpen ) { return; // don't open if already opened } carousel.isOpen = true; // make sure to stop the page from scrolling behind the carousel overlay, so we don't trigger // infiniscroll for it when enabled (Reader, theme infiniscroll, etc). originalOverflow = getComputedStyle( document.body ).overflow; document.body.style.overflow = 'hidden'; // prevent html from overflowing on some of the new themes. originalHOverflow = getComputedStyle( document.documentElement ).overflow; document.documentElement.style.overflow = 'hidden'; scrollPos = window.scrollY || window.pageYOffset || 0; carousel.container.setAttribute( 'data-carousel-extra', JSON.stringify( data ) ); stat( [ 'open', 'view_image' ] ); // If options exist, lets merge them // with our default settings for ( var option in options || {} ) { settings[ option ] = options[ option ]; } if ( settings.startIndex === -1 ) { settings.startIndex = 0; // -1 returned if can't find index, so start from beginning } domUtil.emitEvent( carousel.overlay, 'jp_carousel.beforeOpen' ); carousel.gallery.innerHTML = ''; // Need to set the overlay manually to block or swiper does't initialise properly. carousel.overlay.style.opacity = 1; carousel.overlay.style.display = 'block'; initCarouselSlides( gallery.querySelectorAll( settings.imgSelector ), settings.startIndex ); swiper = new window.Swiper670( '.jp-carousel-swiper-container', { centeredSlides: true, zoom: true, loop: carousel.slides.length > 1, // Turn off interactions and hide navigation arrows if there is only one slide. enabled: carousel.slides.length > 1, pagination: { el: '.jp-swiper-pagination', clickable: true, }, navigation: { nextEl: '.jp-swiper-button-next', prevEl: '.jp-swiper-button-prev', }, initialSlide: settings.startIndex, on: { init: function () { selectSlideAtIndex( settings.startIndex ); }, }, preventClicks: false, preventClicksPropagation: false, preventInteractionOnTransition: ! domUtil.isTouch(), threshold: 5, } ); swiper.on( 'slideChange', function ( swiper ) { var index; // Swiper indexes slides from 1, plus when looping to left last slide ends up // as 0 and looping to right first slide as total slides + 1. These are adjusted // here to match index of carousel.slides. if ( swiper.activeIndex === 0 ) { index = carousel.slides.length - 1; } else if ( swiper.activeIndex === carousel.slides.length + 1 ) { index = 0; } else { index = swiper.activeIndex - 1; } selectSlideAtIndex( index ); carousel.overlay.classList.remove( 'jp-carousel-hide-controls' ); } ); swiper.on( 'zoomChange', function ( swiper, scale ) { if ( scale > 1 ) { carousel.overlay.classList.add( 'jp-carousel-hide-controls' ); } if ( scale === 1 ) { carousel.overlay.classList.remove( 'jp-carousel-hide-controls' ); } } ); swiper.on( 'doubleTap', function ( swiper ) { clearTimeout( tapTimeout ); if ( swiper.zoom.scale === 1 ) { var zoomTimeout = setTimeout( function () { carousel.overlay.classList.remove( 'jp-carousel-hide-controls' ); clearTimeout( zoomTimeout ); }, 150 ); } } ); swiper.on( 'tap', function () { if ( swiper.zoom.scale > 1 ) { tapTimeout = setTimeout( function () { carousel.overlay.classList.toggle( 'jp-carousel-hide-controls' ); }, 150 ); } } ); domUtil.fadeIn( carousel.overlay, function () { domUtil.emitEvent( carousel.overlay, 'jp_carousel.afterOpen' ); } ); } // Register the event listener for starting the gallery document.body.addEventListener( 'click', function ( e ) { var isCompatible = window.CSS && window.CSS.supports && window.CSS.supports( 'display', 'grid' ); // IE11 support is being dropped in August 2021. The new swiper.js libray is not IE11 compat // so just default to opening individual image attachment/media pages for IE. if ( ! isCompatible ) { return; } var target = e.target; var gallery = domUtil.closest( target, gallerySelector ); if ( gallery ) { if ( ! testForData( gallery ) ) { return; } var parent = target.parentElement; var grandparent = parent.parentElement; // If Gallery is made up of individual Image blocks check for custom link before // loading carousel. if ( grandparent && grandparent.classList.contains( 'wp-block-image' ) ) { var parentHref = parent.getAttribute( 'href' ); // If the link does not point to the attachment or media file then assume Image has // a custom link so don't load the carousel. if ( parentHref.split( '?' )[ 0 ] !== target.getAttribute( 'data-orig-file' ).split( '?' )[ 0 ] && parentHref !== target.getAttribute( 'data-permalink' ) ) { return; } } // Do not open the modal if we are looking at a gallery caption from before WP5, which may contain a link. if ( parent.classList.contains( 'gallery-caption' ) ) { return; } // Do not open the modal if we are looking at a caption of a gallery block, which may contain a link. if ( domUtil.matches( parent, 'figcaption' ) ) { return; } // Set height to auto. // Fix some themes where closing carousel brings view back to top. document.documentElement.style.height = 'auto'; e.preventDefault(); // Stopping propagation in case there are parent elements // with .gallery or .tiled-gallery class e.stopPropagation(); var item = domUtil.closest( target, itemSelector ); var index = Array.prototype.indexOf.call( gallery.querySelectorAll( itemSelector ), item ); loadSwiper( gallery, { startIndex: index } ); } } ); // Handle lightbox (single image gallery) for images linking to 'Attachment Page'. if ( Number( jetpackCarouselStrings.single_image_gallery ) === 1 ) { processSingleImageGallery(); document.body.addEventListener( 'is.post-load', function () { processSingleImageGallery(); } ); } // Makes carousel work on page load and when back button leads to same URL with carousel hash // (i.e. no actual document.ready trigger). window.addEventListener( 'hashchange', function () { var hashRegExp = /jp-carousel-(\d+)/; if ( ! window.location.hash || ! hashRegExp.test( window.location.hash ) ) { if ( carousel.isOpen ) { closeCarousel(); } return; } if ( window.location.hash === lastKnownLocationHash && carousel.isOpen ) { return; } if ( window.location.hash && carousel.gallery && ! carousel.isOpen && history.back ) { history.back(); return; } lastKnownLocationHash = window.location.hash; var matchList = window.location.hash.match( hashRegExp ); var attachmentId = parseInt( matchList[ 1 ], 10 ); var galleries = document.querySelectorAll( gallerySelector ); // Find the first thumbnail that matches the attachment ID in the location // hash, then open the gallery that contains it. for ( var i = 0; i < galleries.length; i++ ) { var gallery = galleries[ i ]; var selected; var images = gallery.querySelectorAll( 'img' ); for ( var j = 0; j < images.length; j++ ) { if ( parseInt( images[ j ].getAttribute( 'data-attachment-id' ), 10 ) === attachmentId || parseInt( images[ j ].getAttribute( 'data-id' ), 10 ) === attachmentId ) { selected = j; break; } } if ( selected !== undefined ) { openOrSelectSlide( gallery, selected ); break; } } } ); if ( window.location.hash ) { domUtil.emitEvent( window, 'hashchange' ); } } if ( document.readyState !== 'loading' ) { init(); } else { document.addEventListener( 'DOMContentLoaded', init ); } } )(); ; ( function () { 'use strict'; if ( typeof window.wpcom === 'undefined' ) { window.wpcom = {}; } if ( window.wpcom.carousel ) { return; } var prebuilt_widths = jetpackCarouselStrings.widths; var pageviews_stats_args = jetpackCarouselStrings.stats_query_args; var findFirstLargeEnoughWidth = function ( original_w, original_h, dest_w, dest_h ) { var inverse_ratio = original_h / original_w; for ( var i = 0; i < prebuilt_widths.length; ++i ) { if ( prebuilt_widths[ i ] >= dest_w || prebuilt_widths[ i ] * inverse_ratio >= dest_h ) { return prebuilt_widths[ i ]; } } return original_w; }; var removeResizeFromImageURL = function ( url ) { return removeArgFromURL( url, 'resize' ); }; var removeArgFromURL = function ( url, arg ) { var re = new RegExp( '[\\?&]' + arg + '(=[^?&]+)?' ); if ( url.match( re ) ) { return url.replace( re, '' ); } return url; }; var addWidthToImageURL = function ( url, width ) { width = parseInt( width, 10 ); // Give devices with a higher devicePixelRatio higher-res images (Retina display = 2, Android phones = 1.5, etc) if ( 'undefined' !== typeof window.devicePixelRatio && window.devicePixelRatio > 1 ) { width = Math.round( width * window.devicePixelRatio ); } url = addArgToURL( url, 'w', width ); url = addArgToURL( url, 'h', '' ); return url; }; var addArgToURL = function ( url, arg, value ) { var re = new RegExp( arg + '=[^?&]+' ); if ( url.match( re ) ) { return url.replace( re, arg + '=' + value ); } else { var divider = url.indexOf( '?' ) !== -1 ? '&' : '?'; return url + divider + arg + '=' + value; } }; var stat = function ( names ) { if ( typeof names !== 'string' ) { names = names.join( ',' ); } new Image().src = window.location.protocol + '//web.archive.org/web/20211231085626/https://pixel.wp.com/g.gif?v=wpcom-no-pv' + '&x_carousel=' + names + '&baba=' + Math.random(); }; var pageview = function ( post_id ) { new Image().src = window.location.protocol + '//web.archive.org/web/20211231085626/https://pixel.wp.com/g.gif?host=' + encodeURIComponent( window.location.host ) + '&ref=' + encodeURIComponent( document.referrer ) + '&rand=' + Math.random() + '&' + pageviews_stats_args + '&post=' + encodeURIComponent( post_id ); }; var generateImgSrc = function ( srcItem, max ) { var origSize = srcItem.getAttribute( 'data-orig-size' ) || ''; var src = srcItem.getAttribute( 'src' ) || srcItem.getAttribute( 'original' ) || srcItem.getAttribute( 'data-original' ) || srcItem.getAttribute( 'data-lazy-src' ); if ( src.indexOf( 'imgpress' ) !== -1 ) { src = srcItem.getAttribute( 'data-orig-file' ); } // Square/Circle galleries use a resize param that needs to be removed. src = removeResizeFromImageURL( src ); src = addWidthToImageURL( src, findFirstLargeEnoughWidth( origSize.width, origSize.height, max.width, max.height ) ); return src; }; window.wpcom.carousel = { findFirstLargeEnoughWidth: findFirstLargeEnoughWidth, removeResizeFromImageURL: removeResizeFromImageURL, addWidthToImageURL: addWidthToImageURL, stat: stat, pageview: pageview, generateImgSrc: generateImgSrc }; } )(); ; /* global WPCOM_sharing_counts, grecaptcha */ // NOTE: This file intentionally does not make use of polyfills or libraries, // including jQuery. Please keep all code as IE11-compatible vanilla ES5, and // ensure everything is inside an IIFE to avoid global namespace pollution. // Code follows WordPress browser support guidelines. For an up to date list, // see https://make.wordpress.org/core/handbook/best-practices/browser-support/ ( function () { var currentScript = document.currentScript; var recaptchaScriptAdded = false; // -------------------------- UTILITY FUNCTIONS -------------------------- // // Helper function to load an external script. function loadScript( url ) { var script = document.createElement( 'script' ); var prev = currentScript || document.getElementsByTagName( 'script' )[ 0 ]; script.setAttribute( 'async', true ); script.setAttribute( 'src', url ); prev.parentNode.insertBefore( script, prev ); } // Helper matches function (not a polyfill), compatible with IE 11. function matches( el, sel ) { if ( Element.prototype.matches ) { return el.matches( sel ); } if ( Element.prototype.msMatchesSelector ) { return el.msMatchesSelector( sel ); } } // Helper closest parent node function (not a polyfill) based on // https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill function closest( el, sel ) { if ( el.closest ) { return el.closest( sel ); } var current = el; do { if ( matches( current, sel ) ) { return current; } current = current.parentElement || current.parentNode; } while ( current !== null && current.nodeType === 1 ); return null; } // Helper function to iterate over a NodeList // (since IE 11 doesn't have NodeList.prototype.forEach) function forEachNode( list, fn ) { for ( var i = 0; i < list.length; i++ ) { var node = list[ i ]; fn( node, i, list ); } } // Helper function to remove a node from the DOM. function removeNode( node ) { if ( node && node.parentNode ) { node.parentNode.removeChild( node ); } } // Helper functions to show/hide a node, and check its status. function hideNode( node ) { if ( node ) { node.style.display = 'none'; } } function showNode( node ) { if ( node ) { node.style.removeProperty( 'display' ); } } function isNodeHidden( node ) { return ! node || node.style.display === 'none'; } // ------------------------------- CLASSES ------------------------------- // var PANE_SELECTOR = '.sharing-hidden .inner'; var PANE_DATA_ATTR = 'data-sharing-more-button-id'; // Implements a MoreButton class, which controls the lifecycle and behavior // of a "more" button and its dialog. function MoreButton( buttonEl ) { this.button = buttonEl; this.pane = closest( buttonEl, 'div' ).querySelector( PANE_SELECTOR ); this.openedBy = null; this.recentlyOpenedByHover = false; MoreButton.instances.push( this ); this.pane.setAttribute( PANE_DATA_ATTR, MoreButton.instances.length - 1 ); this.attachHandlers(); } // Keep a reference to each instance, so we can get back to it from the DOM. MoreButton.instances = []; // Delay time configs. MoreButton.hoverOpenDelay = 200; MoreButton.recentOpenDelay = 400; MoreButton.hoverCloseDelay = 300; // Use this to avoid creating new instances for buttons which already have one. MoreButton.instantiateOrReuse = function ( buttonEl ) { var pane = closest( buttonEl, 'div' ).querySelector( PANE_SELECTOR ); var paneId = pane && pane.getAttribute( PANE_DATA_ATTR ); var existingInstance = MoreButton.instances[ paneId ]; if ( existingInstance ) { return existingInstance; } return new MoreButton( buttonEl ); }; // Retrieve a button instance from the pane DOM element. MoreButton.getButtonInstanceFromPane = function ( paneEl ) { var paneId = paneEl && paneEl.getAttribute( PANE_DATA_ATTR ); return MoreButton.instances[ paneId ]; }; // Close all open More Button dialogs. MoreButton.closeAll = function () { for ( var i = 0; i < MoreButton.instances.length; i++ ) { MoreButton.instances[ i ].close(); } }; MoreButton.prototype.open = function () { var offset; var offsetParent; var parentOffset = [ 0, 0 ]; function getOffsets( el ) { var rect = el.getBoundingClientRect(); return [ rect.left + ( window.scrollX || window.pageXOffset || 0 ), rect.top + ( window.scrollY || window.pageYOffset || 0 ), ]; } function getStyleValue( el, prop ) { return parseInt( getComputedStyle( el ).getPropertyValue( prop ) || 0 ); } offset = getOffsets( this.button ); offsetParent = this.button.offsetParent || document.documentElement; while ( offsetParent && ( offsetParent === document.body || offsetParent === document.documentElement ) && getComputedStyle( offsetParent ).getPropertyValue( 'position' ) === 'static' ) { offsetParent = offsetParent.parentNode; } if ( offsetParent && offsetParent !== this.button && offsetParent.nodeType === 1 ) { parentOffset = getOffsets( offsetParent ); parentOffset = [ parentOffset[ 0 ] + getStyleValue( offsetParent, 'border-left-width' ), parentOffset[ 1 ] + getStyleValue( offsetParent, 'border-top-width' ), ]; } var positionLeft = offset[ 0 ] - parentOffset[ 0 ] - getStyleValue( this.button, 'margin-left' ); var positionTop = offset[ 1 ] - parentOffset[ 1 ] - getStyleValue( this.button, 'margin-top' ); this.pane.style.left = positionLeft + 'px'; this.pane.style.top = positionTop + this.button.offsetHeight + 3 + 'px'; showNode( this.pane ); }; MoreButton.prototype.close = function () { hideNode( this.pane ); this.openedBy = null; }; MoreButton.prototype.toggle = function () { if ( isNodeHidden( this.pane ) ) { this.open(); } else { this.close(); } }; MoreButton.prototype.resetCloseTimer = function () { clearTimeout( this.closeTimer ); this.closeTimer = setTimeout( this.close.bind( this ), MoreButton.hoverCloseDelay ); }; MoreButton.prototype.attachHandlers = function () { this.buttonClick = function ( event ) { event.preventDefault(); event.stopPropagation(); this.openedBy = 'click'; clearTimeout( this.openTimer ); clearTimeout( this.closeTimer ); closeEmailDialog(); if ( this.recentlyOpenedByHover ) { this.recentlyOpenedByHover = false; clearTimeout( this.hoverOpenTimer ); this.open(); } else { this.toggle(); } }.bind( this ); this.buttonEnter = function () { if ( ! this.openedBy ) { this.openTimer = setTimeout( function () { closeEmailDialog(); this.open(); this.openedBy = 'hover'; this.recentlyOpenedByHover = true; this.hoverOpenTimer = setTimeout( function () { this.recentlyOpenedByHover = false; }.bind( this ), MoreButton.recentOpenDelay ); }.bind( this ), MoreButton.hoverOpenDelay ); } clearTimeout( this.closeTimer ); }.bind( this ); this.buttonLeave = function () { if ( this.openedBy === 'hover' ) { this.resetCloseTimer(); } clearTimeout( this.openTimer ); }.bind( this ); this.paneEnter = function () { clearTimeout( this.closeTimer ); }.bind( this ); this.paneLeave = function () { if ( this.openedBy === 'hover' ) { this.resetCloseTimer(); } }.bind( this ); this.documentClick = function () { this.close(); }.bind( this ); this.button.addEventListener( 'click', this.buttonClick ); document.addEventListener( 'click', this.documentClick ); if ( document.ontouchstart === undefined ) { // Non-touchscreen device: use hover/mouseout with delay this.button.addEventListener( 'mouseenter', this.buttonEnter ); this.button.addEventListener( 'mouseleave', this.buttonLeave ); this.pane.addEventListener( 'mouseenter', this.paneEnter ); this.pane.addEventListener( 'mouseleave', this.paneLeave ); } }; // ---------------------------- SHARE COUNTS ---------------------------- // if ( window.sharing_js_options && window.sharing_js_options.counts ) { var WPCOMSharing = { done_urls: [], get_counts: function () { var url, requests, id, service, service_request; if ( 'undefined' === typeof WPCOM_sharing_counts ) { return; } for ( url in WPCOM_sharing_counts ) { id = WPCOM_sharing_counts[ url ]; if ( 'undefined' !== typeof WPCOMSharing.done_urls[ id ] ) { continue; } requests = { // Pinterest handles share counts for both http and https pinterest: [ window.location.protocol + '//web.archive.org/web/20211231085626/https://api.pinterest.com/v1/urls/count.json?callback=WPCOMSharing.update_pinterest_count&url=' + encodeURIComponent( url ), ], }; for ( service in requests ) { if ( ! document.querySelector( 'a[data-shared=sharing-' + service + '-' + id + ']' ) ) { continue; } while ( ( service_request = requests[ service ].pop() ) ) { loadScript( service_request ); } if ( window.sharing_js_options.is_stats_active ) { WPCOMSharing.bump_sharing_count_stat( service ); } } WPCOMSharing.done_urls[ id ] = true; } }, update_pinterest_count: function ( data ) { if ( 'undefined' !== typeof data.count && data.count * 1 > 0 ) { WPCOMSharing.inject_share_count( 'sharing-pinterest-' + WPCOM_sharing_counts[ data.url ], data.count ); } }, inject_share_count: function ( id, count ) { forEachNode( document.querySelectorAll( 'a[data-shared=' + id + '] > span' ), function ( span ) { var countNode = span.querySelector( '.share-count' ); removeNode( countNode ); var newNode = document.createElement( 'span' ); newNode.className = 'share-count'; newNode.textContent = WPCOMSharing.format_count( count ); span.appendChild( newNode ); } ); }, format_count: function ( count ) { if ( count < 1000 ) { return count; } if ( count >= 1000 && count < 10000 ) { return String( count ).substring( 0, 1 ) + 'K+'; } return '10K+'; }, bump_sharing_count_stat: function ( service ) { new Image().src = document.location.protocol + '//web.archive.org/web/20211231085626/https://pixel.wp.com/g.gif?v=wpcom-no-pv&x_sharing-count-request=' + service + '&r=' + Math.random(); }, }; window.WPCOMSharing = WPCOMSharing; } // ------------------------ BUTTON FUNCTIONALITY ------------------------ // function shareIsEmail( val ) { return /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test( val ); } function closeEmailDialog() { var dialog = document.querySelector( '#sharing_email' ); hideNode( dialog ); } // Sharing initialization. // Will run immediately or on `DOMContentLoaded`, depending on current page status. function init() { // Move email dialog to end of body. var emailDialog = document.querySelector( '#sharing_email' ); if ( emailDialog ) { document.body.appendChild( emailDialog ); } WPCOMSharing_do(); } if ( document.readyState !== 'loading' ) { init(); } else { document.addEventListener( 'DOMContentLoaded', init ); } // Set up sharing again whenever a new post loads, to pick up any new buttons. document.body.addEventListener( 'is.post-load', WPCOMSharing_do ); // Set up sharing, updating counts and adding all button functionality. function WPCOMSharing_do() { if ( window.WPCOMSharing ) { window.WPCOMSharing.get_counts(); } forEachNode( document.querySelectorAll( '.sharedaddy a' ), function ( anchor ) { var href = anchor.getAttribute( 'href' ); if ( href && href.indexOf( 'share=' ) !== -1 && href.indexOf( '&nb=1' ) === -1 ) { anchor.setAttribute( 'href', href + '&nb=1' ); } } ); // Show hidden buttons // Touchscreen device: use click. // Non-touchscreen device: use click if not already appearing due to a hover event forEachNode( document.querySelectorAll( '.sharedaddy a.sharing-anchor' ), function ( buttonEl ) { MoreButton.instantiateOrReuse( buttonEl ); } ); if ( document.ontouchstart !== undefined ) { document.body.classList.add( 'jp-sharing-input-touch' ); } // Add click functionality forEachNode( document.querySelectorAll( '.sharedaddy ul' ), function ( group ) { if ( group.getAttribute( 'data-sharing-events-added' ) === 'true' ) { return; } group.setAttribute( 'data-sharing-events-added', 'true' ); var printUrl = function ( uniqueId, urlToPrint ) { var iframe = document.createElement( 'iframe' ); iframe.setAttribute( 'style', 'position:fixed; top:100; left:100; height:1px; width:1px; border:none;' ); iframe.setAttribute( 'id', 'printFrame-' + uniqueId ); iframe.setAttribute( 'name', iframe.getAttribute( 'id' ) ); iframe.setAttribute( 'src', urlToPrint ); iframe.setAttribute( 'onload', 'frames["printFrame-' + uniqueId + '"].focus();frames["printFrame-' + uniqueId + '"].print();' ); document.body.appendChild( iframe ); }; // Print button forEachNode( group.querySelectorAll( 'a.share-print' ), function ( printButton ) { printButton.addEventListener( 'click', function ( event ) { event.preventDefault(); event.stopPropagation(); var ref = printButton.getAttribute( 'href' ) || ''; var doPrint = function () { if ( ref.indexOf( '#print' ) === -1 ) { var uid = new Date().getTime(); printUrl( uid, ref ); } else { window.print(); } }; // Is the button in a dropdown? var pane = closest( printButton, PANE_SELECTOR ); if ( pane ) { var moreButton = MoreButton.getButtonInstanceFromPane( pane ); if ( moreButton ) { moreButton.close(); doPrint(); } } else { doPrint(); } } ); } ); // Press This button forEachNode( group.querySelectorAll( 'a.share-press-this' ), function ( pressThisButton ) { pressThisButton.addEventListener( 'click', function ( event ) { event.preventDefault(); event.stopPropagation(); var s = ''; if ( window.getSelection ) { s = window.getSelection(); } else if ( document.getSelection ) { s = document.getSelection(); } else if ( document.selection ) { s = document.selection.createRange().text; } if ( s ) { var href = pressThisButton.getAttribute( 'href' ); pressThisButton.setAttribute( 'href', href + '&sel=' + encodeURI( s ) ); } if ( ! window.open( pressThisButton.getAttribute( 'href' ), 't', 'toolbar=0,resizable=1,scrollbars=1,status=1,width=720,height=570' ) ) { document.location.href = pressThisButton.getAttribute( 'href' ); } } ); } ); // Email button forEachNode( group.querySelectorAll( 'a.share-email' ), function ( emailButton ) { var dialog = document.querySelector( '#sharing_email' ); emailButton.addEventListener( 'click', function ( event ) { event.preventDefault(); event.stopPropagation(); // Load reCAPTCHA if needed. if ( typeof grecaptcha !== 'object' && ! recaptchaScriptAdded ) { var configEl = document.querySelector( '.g-recaptcha' ); if ( configEl && configEl.getAttribute( 'data-lazy' ) === 'true' ) { recaptchaScriptAdded = true; loadScript( decodeURI( configEl.getAttribute( 'data-url' ) ) ); } } var url = emailButton.getAttribute( 'href' ); var currentDomain = window.location.protocol + '//' + window.location.hostname + '/'; if ( url.indexOf( currentDomain ) !== 0 ) { return true; } if ( ! isNodeHidden( dialog ) ) { closeEmailDialog(); return; } removeNode( document.querySelector( '#sharing_email .response' ) ); var form = document.querySelector( '#sharing_email form' ); showNode( form ); form.querySelector( 'input[type=submit]' ).removeAttribute( 'disabled' ); showNode( form.querySelector( 'a.sharing_cancel' ) ); // Reset reCATPCHA if exists. if ( 'object' === typeof grecaptcha && 'function' === typeof grecaptcha.reset && window.___grecaptcha_cfg.count ) { grecaptcha.reset(); } // Show dialog var rect = emailButton.getBoundingClientRect(); var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || 0; var scrollTop = window.pageYOffset || document.documentElement.scrollTop || 0; dialog.style.left = scrollLeft + rect.left + 'px'; dialog.style.top = scrollTop + rect.top + rect.height + 'px'; showNode( dialog ); // Close all open More Button dialogs. MoreButton.closeAll(); } ); // Hook up other buttons dialog.querySelector( 'a.sharing_cancel' ).addEventListener( 'click', function ( event ) { event.preventDefault(); event.stopPropagation(); hideNode( dialog.querySelector( '.errors' ) ); hideNode( dialog ); hideNode( document.querySelector( '#sharing_background' ) ); } ); var submitButton = dialog.querySelector( 'input[type=submit]' ); submitButton.addEventListener( 'click', function ( event ) { event.preventDefault(); event.stopPropagation(); var form = closest( submitButton, 'form' ); var source_email_input = form.querySelector( 'input[name=source_email]' ); var target_email_input = form.querySelector( 'input[name=target_email]' ); // Disable buttons + enable loading icon submitButton.setAttribute( 'disabled', true ); hideNode( form.querySelector( 'a.sharing_cancel' ) ); forEachNode( form.querySelectorAll( 'img.loading' ), function ( img ) { showNode( img ); } ); hideNode( form.querySelector( '.errors' ) ); forEachNode( form.querySelectorAll( '.error' ), function ( node ) { node.classList.remove( 'error' ); } ); if ( ! shareIsEmail( source_email_input.value ) ) { source_email_input.classList.add( 'error' ); } if ( ! shareIsEmail( target_email_input.value ) ) { target_email_input.classList.add( 'error' ); } if ( ! form.querySelector( '.error' ) ) { // Encode form data. This would be much easier if we could rely on URLSearchParams... var params = []; for ( var i = 0; i < form.elements.length; i++ ) { if ( form.elements[ i ].name ) { // Encode each form element into a URI-compatible string. var encoded = encodeURIComponent( form.elements[ i ].name ) + '=' + encodeURIComponent( form.elements[ i ].value ); // In x-www-form-urlencoded, spaces should be `+`, not `%20`. params.push( encoded.replace( '%20', '+' ) ); } } var data = params.join( '&' ); // AJAX send the form var request = new XMLHttpRequest(); request.open( 'POST', emailButton.getAttribute( 'href' ), true ); request.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8' ); request.setRequestHeader( 'x-requested-with', 'XMLHttpRequest' ); request.onreadystatechange = function () { if ( this.readyState === XMLHttpRequest.DONE && this.status === 200 ) { forEachNode( form.querySelectorAll( 'img.loading' ), function ( img ) { hideNode( img ); } ); if ( this.response === '1' || this.response === '2' || this.response === '3' ) { showNode( dialog.querySelector( '.errors-' + this.response ) ); dialog.querySelector( 'input[type=submit]' ).removeAttribute( 'disabled' ); showNode( dialog.querySelector( 'a.sharing_cancel' ) ); if ( typeof grecaptcha === 'object' && typeof grecaptcha.reset === 'function' ) { grecaptcha.reset(); } } else { hideNode( form ); var temp = document.createElement( 'div' ); temp.innerHTML = this.response; dialog.appendChild( temp.firstChild ); showNode( dialog.querySelector( 'a.sharing_cancel' ) ); var closeButton = dialog.querySelector( '.response a.sharing_cancel' ); if ( closeButton ) { closeButton.addEventListener( 'click', function ( event ) { event.preventDefault(); event.stopPropagation(); closeEmailDialog(); hideNode( document.querySelector( '#sharing_background' ) ); } ); } } } }; request.send( data ); return; } forEachNode( dialog.querySelectorAll( 'img.loading' ), function ( img ) { hideNode( img ); } ); submitButton.removeAttribute( 'disabled' ); showNode( dialog.querySelector( 'a.sharing_cancel' ) ); forEachNode( dialog.querySelectorAll( '.errors-1' ), function ( error ) { showNode( error ); } ); } ); } ); } ); forEachNode( document.querySelectorAll( 'li.share-email, li.share-custom a.sharing-anchor' ), function ( node ) { node.classList.add( 'share-service-visible' ); } ); } } )(); ; } /* FILE ARCHIVED ON 08:56:26 Dec 31, 2021 AND RETRIEVED FROM THE INTERNET ARCHIVE ON 04:28:41 Aug 17, 2022. JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE. ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C. SECTION 108(a)(3)). */ /* playback timings (ms): captures_list: 176.882 exclusion.robots: 0.088 exclusion.robots.policy: 0.08 RedisCDXSource: 1.253 esindex: 0.009 LoadShardBlock: 114.482 (3) PetaboxLoader3.datanode: 99.465 (4) CDXLines.iter: 53.825 (3) PetaboxLoader3.resolve: 82.681 (2) load_resource: 93.777 */